Files
red/.planning/phases/18-template-schema-and-crud-api/18-02-SUMMARY.md

103 lines
4.2 KiB
Markdown
Raw Normal View History

---
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*