You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pipeline.config.json enabled database.softDeleteDefault by default, but no
implementation existed — generated projects got a deleted_at column with no
filtering, forcing manual WHERE deleted_at IS NULL in every query.
Add the full pattern:
- templates/snippets/shared/src/db/soft-delete.ts: notDeleted/withNotDeleted/
softDeleteFilter query filters, softDelete() mutation (stamps deleted_at,
guards on IS NULL, returns rows affected), and includeDeletedQuerySchema for
the ?include_deleted=true admin flag
- add nullable deleted_at column to the users schema template
- soft-delete.test.ts: 13 tests asserting against real Drizzle SQL generation
- setup-project.sh copies the helper and test into generated projects
- database-design skill: generate deleted_at columns + indexes and the helper
when softDeleteDefault is true
- route-generation skill: service.delete() calls softDelete(); reads filter
deleted rows; optionalAuth middleware exposes deleted rows to admins only
- tdd-from-schema skill: soft-delete test cases
Verified: tsc passes for both Cloudflare and Node targets; 13/13 tests pass.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| Schema barrel |`api/src/db/schema/index.ts`| Re-exports all schemas |
563
680
| DB connection |`api/src/db/index.ts`| Drizzle client setup |
@@ -583,4 +700,5 @@ cd api && pnpm add -D drizzle-kit drizzle-zod
583
700
-**Zod inference from Drizzle**: Single source of truth -- change the Drizzle schema, the Zod validators follow automatically.
584
701
-**UUID primary keys**: Default for all tables. Avoids sequential ID enumeration attacks and works well in distributed systems.
585
702
-**Soft timestamps**: `createdAt` and `updatedAt` on every table. `$onUpdate` handles automatic timestamp refresh.
703
+
-**Soft deletes by default**: When `database.softDeleteDefault` is true, tables carry a nullable `deleted_at` column and rows are never physically removed. Reads filter `WHERE deleted_at IS NULL` via the generated `soft-delete.ts` helper, and DELETE handlers call `softDelete()`. Admin endpoints can opt back in to deleted rows with `?include_deleted=true`. See [route-generation](../route-generation/SKILL.md) for the handler pattern.
586
704
-**snake_case columns**: PostgreSQL convention. Drizzle maps to camelCase in TypeScript automatically.
0 commit comments