@iron-stack/crud
v1.0.2
Published
Zero-boilerplate CRUD routers for tRPC + Drizzle
Readme
@iron-stack/crud
Zero-boilerplate CRUD routers for tRPC + Drizzle. One function call replaces 60+ lines of repetitive router code.
Install
npm install @iron-stack/crudBefore & After
Before (60+ lines per entity):
export const mealRouter = router({
list: protectedProcedure.input(ListSchema).query(async ({ input, ctx }) => {
const conditions = [];
if (input.cursor) conditions.push(lt(meals.createdAt, new Date(input.cursor)));
const result = await ctx.db.select()
.from(meals).where(and(...conditions))
.orderBy(desc(meals.createdAt)).limit(input.limit + 1);
const hasMore = result.length > input.limit;
// ... 50 more lines of pagination, ownership, serialization
}),
create: protectedProcedure.input(CreateMealSchema).mutation(async ({ input, ctx }) => { /* ... */ }),
update: protectedProcedure.input(/* ... */).mutation(async ({ input, ctx }) => { /* ... */ }),
delete: protectedProcedure.input(/* ... */).mutation(async ({ input, ctx }) => { /* ... */ }),
});After (10 lines):
import { createCrudRouter } from "@iron-stack/crud";
export const mealRouter = createCrudRouter({
router,
protectedProcedure,
db,
table: meals,
createSchema: CreateMealSchema,
ownerField: "userId",
softDelete: true,
});Same result. 5 fully-typed endpoints, cursor pagination, ownership checks, soft delete.
API
createCrudRouter(config)
Returns a tRPC router with these procedures:
| Procedure | Type | Description |
|-----------|------|-------------|
| list | query | Cursor-paginated list, newest first |
| getById | query | Single item by UUID |
| create | mutation | Create with auto owner assignment |
| update | mutation | Update with ownership verification |
| delete | mutation | Delete (soft or hard) with ownership check |
Config
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| table | Drizzle table | required | The database table |
| createSchema | Zod schema | required | Input validation for create |
| updateSchema | Zod schema | createSchema.partial() | Input validation for update |
| ownerField | string | "userId" | Column for ownership filtering |
| cursorField | string | "createdAt" | Column for cursor pagination |
| defaultLimit | number | 20 | Default page size |
| softDelete | boolean | false | Soft delete (set deletedAt) vs hard delete |
Hooks
createCrudRouter({
// ...
hooks: {
beforeCreate: async (data, ctx) => {
// Modify data before insert
return { ...data, slug: slugify(data.title) };
},
afterCreate: async (result, ctx) => {
// Broadcast, send notification, etc.
syncServer.broadcast("meal:new", result);
},
beforeDelete: async (id, ctx) => {
// Clean up related data
},
},
});License
MIT
