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 instanceoverrides
(optional): Custom schema overrides for specific fields
Returns
An object containing the following schemas:
insertSchema
: Zod schema for creating new recordsselectSchema
: Zod schema for reading recordsupdateSchema
: Zod schema for full updatespatchSchema
: 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 instancecustomProcedures
(optional): Additional tRPC procedures to include in the router
-
helpers
: An object with the following properties:createTRPCRouter
: Function to create a tRPC routerpublicProcedure
: tRPC procedure that doesn’t require authenticationprotectedProcedure
: tRPC procedure that requires authentication
Returns
A tRPC router with the following endpoints:
create
: Creates a new recordgetAll
: Retrieves all recordsgetById
: Retrieves a record by IDupdate
: 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 recordsuseGetAll
: Hook for fetching all recordsuseGetById
: Hook for fetching a record by IDuseUpdate
: Hook for full updatesusePatch
: Hook for partial updatesuseDelete
: 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>
);
}