npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zodipus

v0.1.0

Published

Zodipus: solves your schema tragedies - A Prisma generator for clean Zod schemas with custom JSON field support

Readme

Zodipus

Zodipus: solves your schema tragedies.

A powerful Prisma generator that automatically creates:

  1. Clean Zod model schemas (without relations) from your Prisma schema
  2. Enum schemas with TypeScript types
  3. Relation metadata for query engines and GraphQL resolvers
  4. Custom schema support for complex JSON fields
  5. Query Engine for composable, runtime-validated Prisma queries

The Problem: Prisma Types vs. Zod Types

Prisma and Zod generate incompatible types, especially for JSON fields and enums:

import { z } from 'zod';

const user = await prisma.user.findFirst();
// Prisma type: { settings: JsonValue, role: $Enums.UserRole, ... }

function processUser(data: z.infer<typeof UserSchema>) {
  // Zod type: { settings: { theme: 'light' | 'dark', ... }, role: 'USER' | 'ADMIN', ... }
}

// ❌ TypeScript ERROR:
// Type 'JsonValue' is not assignable to type '{ theme?: "light" | "dark" | ... }'
// Type '$Enums.UserRole' is not assignable to type '"USER" | "ADMIN" | "MODERATOR"'
processUser(user);

Why types are incompatible:

  • JSON fields: Prisma uses generic JsonValue, but Zod schemas (via @zodSchema) define specific structures
  • Enums: Prisma generates $Enums.UserRole, while Zod uses literal unions "USER" | "ADMIN"
  • Runtime validation: Even if types matched, Prisma data is not validated — malformed JSON or invalid values can slip through

The Solution: Query Engine

Zodipus's Query Engine creates composable Prisma queries that automatically validate results:

import { createRegistry } from 'zodipus/queryEngine';
import { models, modelRelations } from './generated/generated-index';

const registry = createRegistry({ models, relations: modelRelations });
const userQuery = registry.createQuery('user');

// Build a type-safe query with relations
const query = userQuery({
  select: { id: true, email: true, name: true },
  posts: { select: { title: true, published: true } },
});

// Execute with Prisma
const users = await prisma.user.findMany(query.query);

// ✅ Validate and get properly typed results
const validatedUsers = query.array().parse(users);
// Type: { id: string, email: string, name: string | null, posts: { title: string, published: boolean }[] }[]

// Now safely pass to functions expecting validated data
processUsers(validatedUsers); // ✅ Guaranteed valid!

Installation

# Using npm
npm install -D zodipus

# Using yarn
yarn add -D zodipus

# Using pnpm
pnpm add -D zodipus

Quick Start

1. Add generator to your Prisma schema

generator zodipus {
  provider      = "zodipus"
  output        = "./generated"
  relationDepth = "5"
}

model User {
  id        String   @id @default(uuid())
  email     String   @unique
  name      String?
  role      Role
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

enum Role {
  USER
  ADMIN
}

model Post {
  id        String   @id @default(uuid())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  String
  
  /// @zodSchema PostMetadataSchema
  metadata  Json?
  
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

2. Run Prisma generate

pnpm prisma generate

3. Use generated schemas

import { UserSchema, RoleSchema, PostSchema } from './generated';

// Parse and validate data
const user = UserSchema.parse({
  id: '123',
  email: '[email protected]',
  role: 'ADMIN',
  createdAt: new Date(),
  updatedAt: new Date()
});

// Use enums
const role = RoleSchema.parse('ADMIN');

Features

Clean Zod Schemas

Generate schemas containing only scalar fields - no relation clutter:

export const UserSchema = z.object({
  id: z.string(),
  email: z.string(),
  name: z.string().optional().nullable(),
  role: RoleSchema,
  createdAt: z.coerce.date(),
  updatedAt: z.coerce.date()
});

Custom JSON Field Schemas

Add type-safe validation for JSON fields using @zodSchema annotations:

model Post {
  /// @zodSchema PostMetadataSchema
  metadata Json?
}

Then define in generated/custom-schemas.ts:

export const PostMetadataSchema = z.object({
  tags: z.array(z.string()),
  views: z.number(),
  featured: z.boolean().optional(),
});

Relation Metadata

Extracted automatically for building query engines:

const relations = {
  user: {
    posts: {
      type: "post" as const,
      isArray: true,
      relations: { /* nested */ }
    }
  }
} as const;

Query Engine Integration

Zodipus includes a powerful Query Engine that works seamlessly with your generated schemas:

import { createRegistry } from 'zodipus/queryEngine';
import { models, modelRelations } from './generated/generated-index';

// Create registry
const registry = createRegistry({
  models,
  relations: modelRelations,
});

// Create query builders
export const userQuery = registry.createQuery('user');
export const postQuery = registry.createQuery('post');

// Use with Prisma
const query = userQuery({
  select: { id: true, email: true, name: true },
  posts: {
    select: { title: true, published: true },
  },
});

// Execute and validate
const users = await prisma.user.findMany(query.query);
const validated = query.array().parse(users);

Features:

  • ✅ Complete compile-time type safety
  • ✅ Runtime validation with Zod
  • ✅ Automatic relation handling
  • ✅ Nested query support
  • ✅ Selective validation for performance

See Query Engine Documentation for complete API reference and examples.

Configuration

Add options to your Prisma generator configuration:

generator zodipus {
  provider           = "zodipus"
  output             = "./generated"
  relationDepth      = "5"
  dateFormat         = "coerce"    // or "string"
  passthroughEnabled = "false"     // or "true"
  debug              = "false"     // or "true"
}

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | provider | string | Required | Set to "zodipus" | | output | string | Required | Output directory for generated files | | relationDepth | string | "5" | Maximum depth for nested relations | | dateFormat | string | "coerce" | DateTime handling: "coerce" uses z.coerce.date(), "string" uses z.string().datetime() | | passthroughEnabled | string | "false" | When "true", objects allow extra keys (passthrough mode). When "false" (default), unknown keys are stripped. | | debug | string | "false" | Enable debug logging during generation |

DateTime Format

Choose how DateTime fields are validated:

  • coerce (default): Uses z.coerce.date() - accepts Date objects, ISO strings, and timestamps
  • string: Uses z.string().datetime() - only accepts ISO 8601 datetime strings
// dateFormat = "coerce" (default)
generator zodipus {
  dateFormat = "coerce"
}
// Generates: createdAt: z.coerce.date()

// dateFormat = "string"
generator zodipus {
  dateFormat = "string"
}
// Generates: createdAt: z.string().datetime()

Passthrough Mode

Control how objects handle extra properties:

  • false (default): Strict mode - unknown keys are stripped during parsing
  • true: Passthrough mode - unknown keys are preserved
// passthroughEnabled = "false" (default)
generator zodipus {
  passthroughEnabled = "false"
}
// Generates: z.object({ ... })

// passthroughEnabled = "true"
generator zodipus {
  passthroughEnabled = "true"
}
// Generates: z.object({ ... }).passthrough()

Generated Files

generated/
├── enums.ts              # Prisma enums as Zod schemas
├── models.ts             # Clean model schemas
├── custom-schemas.ts     # Custom JSON field schemas
├── generated-relations.ts # Relation metadata
└── generated-index.ts    # Exports

Examples

API Validation

import { UserSchema } from './generated';

app.post('/users', (req, res) => {
  const user = UserSchema.parse(req.body);
  await db.user.create({ data: user });
});

Partial Updates

const UpdateUserSchema = UserSchema.partial();
const updates = UpdateUserSchema.parse(req.body);

API Reference

Main Exports (zodipus)

| Export | Type | Description | |--------|------|-------------| | createRegistry | Function | Creates a query registry for type-safe Prisma queries |

Query Engine (zodipus/queryEngine)

| Export | Type | Description | |--------|------|-------------| | createRegistry(config) | Function | Creates query registry with models and relations | | ModelRegistry | Type | Generic type for model schema map | | ModelRelations | Type | Generic type for relation metadata | | RelationConfig | Type | Configuration for a single relation | | QueryExecutor | Type | Return type of query builders | | SafeParseResult<T> | Type | Result of .safeParse() operations |

createRegistry(config)

function createRegistry<TModels, TRelations>(config: {
  models: TModels;
  relations: TRelations;
}): {
  createQuery: (model: keyof TModels) => QueryBuilder;
};

QueryExecutor

interface QueryExecutor<T> {
  query: { select: ... } | { include: ... };  // Pass to Prisma
  parse(data: unknown): T;                     // Throws on invalid
  safeParse(data: unknown): SafeParseResult<T>; // Returns {success, data/error}
  array(): {
    parse(data: unknown[]): T[];
    safeParse(data: unknown[]): SafeParseResult<T[]>;
  };
}

Errors (zodipus/errors)

| Export | Type | Description | |--------|------|-------------| | ZodipusError | Class | Base error class | | ZodipusValidationError | Class | Thrown when validation fails | | ZodipusGeneratorError | Class | Thrown during schema generation | | ValidationErrorContext | Type | Context included in validation errors |

ZodipusValidationError

class ZodipusValidationError extends ZodipusError {
  context: {
    model: string;      // Model being validated
    field?: string;     // Field that failed
    expected?: string;  // Expected type/value
    received?: string;  // Received type/value
    path?: (string | number)[];  // Path to field
  };
}

Generated Files

| File | Exports | |------|---------| | models.ts | UserSchema, PostSchema, etc. (Zod schemas for each model) | | enums.ts | RoleSchema, StatusSchema, etc. (Zod schemas for enums) | | generated-relations.ts | modelRelations (relation metadata for Query Engine) | | generated-index.ts | Re-exports all above + models namespace | | custom-schemas.ts | Your custom JSON field schemas (created once, not overwritten) |

CLI

# Inspect schema structure
zodipus inspect <schema.prisma> [--models] [--enums] [--relations] [--json]

# Generate with dry-run
zodipus generate <schema.prisma> [--dry-run]