| 18-template-schema-and-crud-api |
02 |
api |
| drizzle |
| next-app-router |
| rest-api |
| soft-delete |
| postgresql |
|
| phase |
provides |
| 18-01 |
documentTemplates Drizzle table + 0012 migration |
|
|
| GET /api/templates — list active templates with formName JOIN and computed fieldCount |
| POST /api/templates — create template with name + formTemplateId FK validation |
| PATCH /api/templates/[id] — rename and/or update signatureFields with explicit updatedAt |
| DELETE /api/templates/[id] — soft-delete via archivedAt (no hard delete) |
|
| Phase 19 template editor UI |
| Phase 20 apply-template endpoint |
|
| added |
patterns |
|
|
| soft-delete filter (isNull archivedAt) |
| LEFT JOIN for computed field |
| explicit updatedAt on every UPDATE |
|
|
| created |
modified |
| teressa-copeland-homes/src/app/api/templates/route.ts |
| teressa-copeland-homes/src/app/api/templates/[id]/route.ts |
|
|
|
| fieldCount derived server-side as (signatureFields ?? []).length — not stored in DB per D-12 |
| PATCH guards against archived templates via isNull(archivedAt) before update — archived templates return 404 |
| DELETE sets archivedAt + updatedAt atomically in a single UPDATE statement, returns 204 No Content |
| POST validates FK existence via db.query.formTemplates.findFirst before INSERT — returns 404 not 500 on bad FK |
|
| Soft-delete list filter: .where(isNull(documentTemplates.archivedAt)) in GET handler |
| Soft-delete guard in mutating handlers: findFirst with and(eq(id), isNull(archivedAt)) → 404 if missing/archived |
| Explicit updatedAt: new Date() in every UPDATE per D-05 pattern |
|
| TMPL-01 |
| TMPL-02 |
| TMPL-03 |
| TMPL-04 |
|
3min |
2026-04-06 |