103 lines
4.2 KiB
Markdown
103 lines
4.2 KiB
Markdown
|
|
---
|
||
|
|
phase: 18-template-schema-and-crud-api
|
||
|
|
plan: "02"
|
||
|
|
subsystem: api
|
||
|
|
tags: [drizzle, next-app-router, rest-api, soft-delete, postgresql]
|
||
|
|
|
||
|
|
requires:
|
||
|
|
- phase: 18-01
|
||
|
|
provides: documentTemplates Drizzle table + 0012 migration
|
||
|
|
|
||
|
|
provides:
|
||
|
|
- 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)
|
||
|
|
|
||
|
|
affects: [Phase 19 template editor UI, Phase 20 apply-template endpoint]
|
||
|
|
|
||
|
|
tech-stack:
|
||
|
|
added: []
|
||
|
|
patterns: [soft-delete filter (isNull archivedAt), LEFT JOIN for computed field, explicit updatedAt on every UPDATE]
|
||
|
|
|
||
|
|
key-files:
|
||
|
|
created:
|
||
|
|
- teressa-copeland-homes/src/app/api/templates/route.ts
|
||
|
|
- teressa-copeland-homes/src/app/api/templates/[id]/route.ts
|
||
|
|
modified: []
|
||
|
|
|
||
|
|
key-decisions:
|
||
|
|
- "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"
|
||
|
|
|
||
|
|
patterns-established:
|
||
|
|
- "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"
|
||
|
|
|
||
|
|
requirements-completed: [TMPL-01, TMPL-02, TMPL-03, TMPL-04]
|
||
|
|
|
||
|
|
duration: 3min
|
||
|
|
completed: 2026-04-06
|
||
|
|
---
|
||
|
|
|
||
|
|
# Phase 18 Plan 02: Template CRUD API Summary
|
||
|
|
|
||
|
|
**Four REST handlers at /api/templates providing full CRUD with soft-delete, auth guard, FK validation, and computed fieldCount via LEFT JOIN on formTemplates.**
|
||
|
|
|
||
|
|
## Performance
|
||
|
|
|
||
|
|
- **Duration:** ~3 min
|
||
|
|
- **Started:** 2026-04-06T18:16:47Z
|
||
|
|
- **Completed:** 2026-04-06T18:19:30Z
|
||
|
|
- **Tasks:** 2
|
||
|
|
- **Files modified:** 2
|
||
|
|
|
||
|
|
## Accomplishments
|
||
|
|
|
||
|
|
- GET /api/templates lists active templates only (archivedAt IS NULL) with formName from joined formTemplates and computed fieldCount
|
||
|
|
- POST /api/templates validates name + formTemplateId, checks FK exists in formTemplates, inserts and returns 201
|
||
|
|
- PATCH /api/templates/[id] handles rename and signatureFields update with explicit updatedAt, guards archived templates
|
||
|
|
- DELETE /api/templates/[id] soft-deletes via archivedAt = new Date() per D-07, returns 204 — never removes the row
|
||
|
|
|
||
|
|
## Task Commits
|
||
|
|
|
||
|
|
1. **Task 1: Create GET and POST routes at /api/templates** - `28c7773` (feat)
|
||
|
|
2. **Task 2: Create PATCH and DELETE routes at /api/templates/[id]** - `12a74fc` (feat)
|
||
|
|
|
||
|
|
## Files Created/Modified
|
||
|
|
|
||
|
|
- `teressa-copeland-homes/src/app/api/templates/route.ts` — GET (list active templates with JOIN) and POST (create with FK validation)
|
||
|
|
- `teressa-copeland-homes/src/app/api/templates/[id]/route.ts` — PATCH (rename/update fields, explicit updatedAt) and DELETE (soft-delete via archivedAt)
|
||
|
|
|
||
|
|
## Decisions Made
|
||
|
|
|
||
|
|
- PATCH returns 404 for archived templates (guards via `and(eq(id), isNull(archivedAt))`) — ensures mutating handlers do not resurrect archived templates accidentally
|
||
|
|
- POST verifies `formTemplateId` FK exists before INSERT, returning 404 with a clear error message rather than letting the DB constraint throw a 500
|
||
|
|
- DELETE returns 204 No Content on success (no body) — matches REST convention for soft-delete confirmation
|
||
|
|
|
||
|
|
## Deviations from Plan
|
||
|
|
|
||
|
|
None - plan executed exactly as written.
|
||
|
|
|
||
|
|
## Issues Encountered
|
||
|
|
|
||
|
|
None.
|
||
|
|
|
||
|
|
## User Setup Required
|
||
|
|
|
||
|
|
None - no external service configuration required.
|
||
|
|
|
||
|
|
## Next Phase Readiness
|
||
|
|
|
||
|
|
- All four CRUD routes are ready for Phase 19 (template editor UI)
|
||
|
|
- Phase 19 will call GET to list templates, POST to create, PATCH with signatureFields to save field layout, and optionally DELETE
|
||
|
|
- Phase 20 (apply template) will call GET to pick a template, then read signatureFields to stamp fresh UUIDs at apply time
|
||
|
|
- No blockers. TypeScript compiles clean.
|
||
|
|
|
||
|
|
---
|
||
|
|
*Phase: 18-template-schema-and-crud-api*
|
||
|
|
*Completed: 2026-04-06*
|