skills/bratsos/zodipus/zodipus-query-engine

zodipus-query-engine

SKILL.md

Query Engine

Build validated, type-safe Prisma queries with automatic result validation.

When to Apply

  • User mentions "query engine" or "createRegistry"
  • User wants type-safe Prisma queries
  • User asks about select/include with validation
  • User needs to validate Prisma query results
  • User mentions "composable queries"
  • User asks about relation queries with Zod

Core Concept

The Query Engine creates composable query builders that:

  1. Generate Prisma query objects (select, include)
  2. Provide matching Zod schemas for validation
  3. Infer TypeScript types automatically
User Request → Query Builder → Prisma Query + Zod Schema → Validated Result

Setup

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

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

// Create query builders for each model you need
const userQuery = registry.createQuery('user');
const postQuery = registry.createQuery('post');
const commentQuery = registry.createQuery('comment');

Basic Patterns

Pattern 1: Select Specific Fields

Select only the fields you need. Validation matches your selection.

const query = userQuery({
  select: { id: true, email: true, name: true }
});

// query.query = { select: { id: true, email: true, name: true } }
const user = await prisma.user.findFirst(query.query);

// Validates and types only selected fields
const validated = query.parse(user);
// Type: { id: string; email: string; name: string | null }

Pattern 2: Include Relations

Include related records with their own field selection.

const query = userQuery({
  select: { id: true, email: true },
  posts: {
    select: { id: true, title: true, published: true }
  }
});

const users = await prisma.user.findMany(query.query);
const validated = query.array().parse(users);
// Type: {
//   id: string;
//   email: string;
//   posts: { id: string; title: string; published: boolean }[]
// }[]

Pattern 3: Nested Relations

Chain relations as deeply as your relationDepth allows.

const query = userQuery({
  select: { id: true, name: true },
  posts: {
    select: { title: true },
    comments: {
      select: { content: true, createdAt: true },
      author: {
        select: { name: true, email: true }
      }
    }
  }
});

// 4 levels deep: User → posts → comments → author

Pattern 4: Array Results (findMany)

Use .array() for validating arrays of results.

const query = userQuery({
  select: { id: true, email: true }
});

const users = await prisma.user.findMany(query.query);

// Validate array
const validated = query.array().parse(users);
// Type: { id: string; email: string }[]

Pattern 5: Safe Parsing

Handle validation errors without throwing.

const result = query.safeParse(data);

if (result.success) {
  // result.data is fully typed
  console.log(result.data.email);
} else {
  // result.error is ZodError
  console.error(result.error.issues);
  console.error(result.error.format()); // Formatted errors
}

Pattern 6: Partial Validation

For PATCH endpoints or partial updates.

const query = userQuery({
  select: { id: true, email: true, name: true }
});

// Make all fields optional
const partialQuery = query.partial();

const updates = partialQuery.parse({ name: 'New Name' });
// Type: { id?: string; email?: string; name?: string | null }

Advanced Patterns

Combining with Prisma Where/OrderBy

The Query Engine generates select/include objects. Combine with your own conditions:

const query = userQuery({
  select: { id: true, email: true, name: true },
  posts: { select: { title: true } }
});

// Add where, orderBy, pagination
const users = await prisma.user.findMany({
  ...query.query,
  where: { role: 'ADMIN' },
  orderBy: { createdAt: 'desc' },
  take: 10,
  skip: 0,
});

const validated = query.array().parse(users);

Reusable Query Fragments

Create reusable query configurations:

// Define reusable configs
const minimalUser = { select: { id: true, email: true } } as const;
const fullUser = {
  select: { id: true, email: true, name: true, role: true },
  posts: { select: { id: true, title: true } }
} as const;

// Use them
const minimalQuery = userQuery(minimalUser);
const fullQuery = userQuery(fullUser);

Conditional Relations

Include relations conditionally:

function getUserQuery(includePosts: boolean) {
  const base = { select: { id: true, email: true, name: true } };

  if (includePosts) {
    return userQuery({
      ...base,
      posts: { select: { id: true, title: true } }
    });
  }

  return userQuery(base);
}

Type Extraction

Extract types from queries:

import { z } from 'zod';

const query = userQuery({
  select: { id: true, email: true },
  posts: { select: { title: true } }
});

// Extract the validated type
type UserWithPosts = z.infer<ReturnType<typeof query.parse>>;
// or
type UserWithPosts = z.output<typeof query>;

API Reference

createRegistry(config)

Creates a registry for building queries.

const registry = createRegistry({
  models,           // From generated-index.ts
  relations: modelRelations,  // From generated-index.ts
});

registry.createQuery(modelName)

Returns a query builder function for the specified model.

const userQuery = registry.createQuery('user');
const postQuery = registry.createQuery('post');

Query Builder Return Object

const query = userQuery({ select: { id: true } });

query.query       // Prisma query object: { select: { id: true } }
query.parse()     // Validates single result (throws on error)
query.safeParse() // Validates single result (returns result object)
query.array()     // Returns schema for validating arrays
query.partial()   // Returns schema with all fields optional

Priority Patterns

Priority Pattern When to Use
Critical query.parse(result) Validate findFirst/findUnique results
Critical query.array().parse(results) Validate findMany results
High query.safeParse(result) When you need error handling
High Nested relations Querying with related data
Medium query.partial() PATCH endpoints, partial updates
Medium Combining with where/orderBy Filtered queries
Low Type extraction Advanced TypeScript usage

Common Mistakes

Mistake 1: Forgetting .array() for findMany

// Wrong - will fail validation
const users = await prisma.user.findMany(query.query);
const validated = query.parse(users); // Error: expected object, got array

// Correct
const validated = query.array().parse(users);

Mistake 2: Using query.query as the whole argument

// Wrong - overwrites your conditions
const user = await prisma.user.findFirst(query.query); // OK
const user = await prisma.user.findFirst({
  query.query, // Syntax error!
  where: { id: '123' }
});

// Correct - spread the query
const user = await prisma.user.findFirst({
  ...query.query,
  where: { id: '123' }
});

Mistake 3: Accessing unselected fields

const query = userQuery({ select: { id: true } });
const user = query.parse(result);

// Wrong - 'email' wasn't selected
console.log(user.email); // TypeScript error

// Correct - add 'email' to select
const query = userQuery({ select: { id: true, email: true } });

For more patterns, see references/QUERY-PATTERNS.md.

Weekly Installs
3
Repository
bratsos/zodipus
GitHub Stars
1
First Seen
Feb 6, 2026
Installed on
opencode3
gemini-cli3
antigravity3
claude-code3
github-copilot3
codex3