Ryan Mitchell·
Full CRUD for our invoices resource - validation, cursor pagination, optimistic locking, done
Generates a complete CRUD REST API for any resource with validation, pagination, filtering, sorting, and authorization.
Full CRUD API Generator with Validation
You are a backend engineer generating a complete CRUD API for a resource. Build all layers.
## Resource Spec
- Resource name: {{resource_name}}
- Fields:
```
{{field_definitions}}
```
- Language/Framework: {{framework}}
- Database: {{database}}
- ORM/Query builder: {{orm}}
## CRUD Requirements
Generate complete implementations for:
### CREATE (POST /{{resource_name}}s)
- Request validation (type, format, range, uniqueness)
- Business rule validation
- Transaction-wrapped creation
- Return 201 with created resource
- Location header
### READ LIST (GET /{{resource_name}}s)
- Pagination (cursor-based or offset with limit)
- Filtering (eq, ne, gt, lt, contains, in, date range)
- Sorting (multi-column, asc/desc)
- Field selection (sparse fieldsets)
- Search (full-text if applicable)
- Return 200 with paginated result envelope
### READ ONE (GET /{{resource_name}}s/:id)
- 404 handling for missing resources
- Return 200 with resource
### UPDATE (PATCH /{{resource_name}}s/:id)
- Partial update support
- Optimistic locking (ETag/If-Match or version field)
- Validation same as create
- Return 200 with updated resource
### DELETE (DELETE /{{resource_name}}s/:id)
- Soft delete (deleted_at) or hard delete based on spec
- Cascade behavior documentation
- Return 204 No Content
## Additional Files
1. **Validation Schema** - Zod/Joi/yup/class-validator schema
2. **DTOs** - Request/response types
3. **Repository** - Data access layer
4. **Service** - Business logic
5. **Controller/Routes** - HTTP handling
6. **Tests** - All CRUD operations with edge cases
7. **OpenAPI Spec** - YAML/JSON spec for these endpoints
Follow REST conventions. Use proper HTTP status codes. Handle 400, 401, 403, 404, 409, 422, 500 appropriately.
Ergebnisse
Full CRUD for the `invoice` resource — Express + TypeScript + Drizzle on Postgres. Validation, cursor pagination, optimistic locking.
```typescript
// validation.ts
export const createInvoice = z.object({
customerId: z.string().uuid(),
amountCents: z.number().int().positive(),
dueDate: z.coerce.date(),
});
// routes.ts
router.post("/invoices", requireAuth, async (req, res) => {
const input = createInvoice.parse(req.body);
const [row] = await db.insert(invoices)
.values({ ...input, ownerId: req.user.id }).returning();
res.status(201).location(`/invoices/${row.id}`).json({ data: row });
});
router.get("/invoices", requireAuth, async (req, res) => {
const { cursor, status, sort = "createdAt:desc" } = req.query;
const rows = await listInvoices({ ownerId: req.user.id, cursor, status, sort });
res.json({ data: rows, nextCursor: rows.at(-1)?.id ?? null });
});
router.patch("/invoices/:id", requireAuth, async (req, res) => {
const result = await db.update(invoices)
.set({ ...req.body, version: sql`version + 1` })
.where(and(
eq(invoices.id, req.params.id),
eq(invoices.version, Number(req.headers["if-match"])), // optimistic lock
)).returning();
if (!result.length) return res.status(409).json({ error: "version_conflict" });
res.json({ data: result[0] });
});
```
**Status codes:** 201 create (+ `Location`), 200 list/read/update, 204 delete (soft, sets `deletedAt`), 404 missing, 409 stale `If-Match`, 422 validation. List supports `eq/in/date-range` filters and multi-column sort. OpenAPI YAML for all five routes is generated alongside.
Modell: Claude Sonnet 4
5 Likes2 SavesScore: 4