Skip to Content

Schema API

CrudKit’s schema utilities help you generate Zod validation schemas for your Drizzle ORM tables.

generateSchemas

The generateSchemas function creates a set of Zod validation schemas for a Drizzle table, which are used for validating data in CRUD operations.

Signature

function generateSchemas<T extends Table>(config: TableConfig<T>): { insertSchema: z.ZodType; selectSchema: z.ZodType; updateSchema: z.ZodType; patchSchema: z.ZodType; }

Parameters

  • config: A configuration object with the following properties:
    • table: A Drizzle ORM table instance
    • overrides (optional): Custom schema overrides for specific fields

Returns

An object containing the following schemas:

  • insertSchema: Zod schema for creating new records
  • selectSchema: Zod schema for reading records
  • updateSchema: Zod schema for full updates
  • patchSchema: Zod schema for partial updates

Example

import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core'; import { generateSchemas } from 'crudkit'; import { z } from 'zod'; const users = pgTable('users', { id: serial('id').primaryKey(), name: text('name').notNull(), email: text('email').notNull(), createdAt: timestamp('created_at').defaultNow(), }); // Generate default schemas const userSchemas = generateSchemas({ table: users, }); // With custom overrides const userSchemasWithOverrides = generateSchemas({ table: users, overrides: { insert: { name: z.string().min(2).max(50), email: z.string().email(), }, }, });

Usage with tRPC Router

The schemas generated by generateSchemas are used internally by the generateRouter function when creating tRPC routers. They validate request inputs for your API endpoints.

// This is handled automatically when using generateRouter const router = generateRouter( { table: users, }, helpers );

Custom Validation

You can extend the generated schemas to add custom validation:

const { insertSchema } = generateSchemas({ table: users, }); const customInsertSchema = insertSchema.extend({ password: z.string().min(8), confirmPassword: z.string().min(8), }).refine(data => data.password === data.confirmPassword, { message: "Passwords don't match", path: ["confirmPassword"], });
Next, let's create the tRPC API documentation: ```markdown:apps/docs/content/api/trpc.mdx --- title: "tRPC Integration" description: "Documentation for CrudKit's tRPC router generation." --- # tRPC Integration CrudKit provides a powerful function to generate type-safe tRPC routers for your Drizzle tables. ## `generateRouter` The `generateRouter` function creates a tRPC router with standard CRUD operations for a Drizzle table. ### Signature ```typescript function generateRouter< TTable extends Table, TContext extends { db: ReturnType<typeof drizzle> }, TTrpc >( config: RouterConfig<TTable>, helpers: { createTRPCRouter: TTrpc["router"]; publicProcedure: TTrpc["procedure"]; protectedProcedure: TTrpc["procedure"]; } ): ReturnType<TTrpc["router"]>

Parameters

  • config: A configuration object with the following properties:

    • table: A Drizzle ORM table instance
    • customProcedures (optional): Additional tRPC procedures to include in the router
  • helpers: An object with the following properties:

    • createTRPCRouter: Function to create a tRPC router
    • publicProcedure: tRPC procedure that doesn’t require authentication
    • protectedProcedure: tRPC procedure that requires authentication

Returns

A tRPC router with the following endpoints:

  • create: Creates a new record
  • getAll: Retrieves all records
  • getById: Retrieves a record by ID
  • update: Updates a record (full replacement)
  • patch: Updates a record (partial update)
  • delete: Deletes a record

Example

import { createTRPCRouter, publicProcedure, protectedProcedure } from '../trpc'; import { generateRouter } from 'crudkit'; import { db } from '../db'; import { users } from '../schema'; export const usersRouter = generateRouter( { table: users, }, { createTRPCRouter, publicProcedure, protectedProcedure, } );

Authentication

By default, CrudKit configures the following authentication levels for the CRUD operations:

  • Public (no auth required):

    • getAll
    • getById
  • Protected (auth required):

    • create
    • update
    • patch
    • delete

Custom Procedures

You can add custom procedures to your router:

export const usersRouter = generateRouter( { table: users, customProcedures: { search: publicProcedure .input(z.object({ query: z.string() })) .query(async ({ input, ctx }) => { return await ctx.db .select() .from(users) .where(like(users.name, `%${input.query}%`)) .limit(10); }), countActive: publicProcedure .query(async ({ ctx }) => { const result = await ctx.db .select({ count: sql`count(*)` }) .from(users) .where(eq(users.status, 'active')); return result[0].count; }), }, }, helpers );
Finally, let's create the React Query documentation: ```markdown:apps/docs/content/api/react-query.mdx --- title: "React Query Hooks" description: "Documentation for CrudKit's React Query hooks generation." --- # React Query Hooks CrudKit provides a utility to generate React Query hooks for your tRPC routers, making it easy to use the CRUD operations in your React components. ## `generateReactQueryHooks` The `generateReactQueryHooks` function creates a set of React Query hooks for a tRPC router. ### Signature ```typescript function generateReactQueryHooks<T extends Table>( config: ReactQueryConfig<T>, trpc: any ): { useCreate: () => CreateHookResult; useGetAll: () => GetAllHookResult; useGetById: (id: string) => GetByIdHookResult; useUpdate: () => UpdateHookResult; usePatch: () => PatchHookResult; useDelete: () => DeleteHookResult; }

Parameters

  • config: A configuration object with the following properties:

    • routerPath: The path to the tRPC router (should match the table name)
  • trpc: Your tRPC instance

Returns

An object containing the following hooks:

  • useCreate: Hook for creating records
  • useGetAll: Hook for fetching all records
  • useGetById: Hook for fetching a record by ID
  • useUpdate: Hook for full updates
  • usePatch: Hook for partial updates
  • useDelete: Hook for deletions

Example

import { generateReactQueryHooks } from 'crudkit'; import { trpc } from '../utils/trpc'; import { users } from '../schema'; export const useUsers = generateReactQueryHooks( { routerPath: 'users', }, trpc );

Usage in Components

Here’s how to use the generated hooks in your React components:

Fetching Data

import { useUsers } from '../hooks/use-users'; function UsersList() { const { data, isLoading, error } = useUsers.useGetAll(); if (isLoading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <ul> {data?.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }

Creating Data

import { useUsers } from '../hooks/use-users'; function CreateUserForm() { const { create, isLoading, error } = useUsers.useCreate(); const handleSubmit = (event) => { event.preventDefault(); const formData = new FormData(event.target); create({ name: formData.get('name'), email: formData.get('email'), }); }; return ( <form onSubmit={handleSubmit}> <input name="name" placeholder="Name" /> <input name="email" placeholder="Email" /> <button type="submit" disabled={isLoading}> {isLoading ? 'Creating...' : 'Create User'} </button> {error && <div>Error: {error.message}</div>} </form> ); }

Updating Data

import { useUsers } from '../hooks/use-users'; function UpdateUserForm({ user }) { const { update, isLoading } = useUsers.useUpdate(); const handleSubmit = (event) => { event.preventDefault(); const formData = new FormData(event.target); update({ id: user.id, name: formData.get('name'), email: formData.get('email'), }); }; return ( <form onSubmit={handleSubmit}> <input name="name" defaultValue={user.name} /> <input name="email" defaultValue={user.email} /> <button type="submit" disabled={isLoading}> {isLoading ? 'Updating...' : 'Update User'} </button> </form> ); }

Deleting Data

import { useUsers } from '../hooks/use-users'; function DeleteUserButton({ userId }) { const { delete: deleteUser, isLoading } = useUsers.useDelete(); const handleDelete = () => { if (confirm('Are you sure you want to delete this user?')) { deleteUser({ id: userId }); } }; return ( <button onClick={handleDelete} disabled={isLoading}> {isLoading ? 'Deleting...' : 'Delete User'} </button> ); }
Last updated on