@withmata/blueprints 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.claude/commands/audit.md +179 -0
  2. package/.claude/commands/discover.md +92 -0
  3. package/.claude/commands/new-blueprint.md +265 -0
  4. package/.claude/commands/new-project.md +230 -0
  5. package/.claude/commands/scaffold-auth.md +310 -0
  6. package/.claude/commands/scaffold-db.md +270 -0
  7. package/.claude/commands/scaffold-foundation.md +158 -0
  8. package/.cursor/commands/audit.md +179 -0
  9. package/.cursor/commands/discover.md +92 -0
  10. package/.cursor/commands/new-blueprint.md +265 -0
  11. package/.cursor/commands/new-project.md +230 -0
  12. package/.cursor/commands/scaffold-auth.md +310 -0
  13. package/.cursor/commands/scaffold-db.md +270 -0
  14. package/.cursor/commands/scaffold-foundation.md +158 -0
  15. package/.opencode/commands/audit.md +183 -0
  16. package/.opencode/commands/discover.md +96 -0
  17. package/.opencode/commands/new-blueprint.md +269 -0
  18. package/.opencode/commands/new-project.md +234 -0
  19. package/.opencode/commands/scaffold-auth.md +314 -0
  20. package/.opencode/commands/scaffold-db.md +274 -0
  21. package/.opencode/commands/scaffold-foundation.md +162 -0
  22. package/ENGINEERING.md +47 -0
  23. package/blueprints/discovery/product-discovery/BLUEPRINT.md +361 -0
  24. package/blueprints/discovery/product-discovery/templates/archetype-template.md +143 -0
  25. package/blueprints/discovery/product-discovery/templates/product-brief.md +65 -0
  26. package/blueprints/discovery/product-discovery/templates/product-thesis.md +64 -0
  27. package/blueprints/discovery/product-discovery/templates/research-summary.md +43 -0
  28. package/blueprints/features/auth-better-auth/BLUEPRINT.md +794 -0
  29. package/blueprints/features/auth-better-auth/files/client/auth-client.ts +31 -0
  30. package/blueprints/features/auth-better-auth/files/client/context/organization.ts +236 -0
  31. package/blueprints/features/auth-better-auth/files/client/hooks/use-create-organization.ts +45 -0
  32. package/blueprints/features/auth-better-auth/files/client/hooks/use-has-permission.ts +26 -0
  33. package/blueprints/features/auth-better-auth/files/client/hooks/use-organization.ts +64 -0
  34. package/blueprints/features/auth-better-auth/files/client/schema/auth.ts +21 -0
  35. package/blueprints/features/auth-better-auth/files/client/schema/organization.ts +51 -0
  36. package/blueprints/features/auth-better-auth/files/client/types/organization.ts +20 -0
  37. package/blueprints/features/auth-better-auth/files/db/auth-schema.ts +184 -0
  38. package/blueprints/features/auth-better-auth/files/db/drizzle.config.ts +21 -0
  39. package/blueprints/features/auth-better-auth/files/middleware/route-protection.ts +84 -0
  40. package/blueprints/features/auth-better-auth/files/server/auth-db.ts +18 -0
  41. package/blueprints/features/auth-better-auth/files/server/auth.ts +159 -0
  42. package/blueprints/features/auth-better-auth/files/server/env.ts +44 -0
  43. package/blueprints/features/auth-better-auth/files/server/middleware.ts +144 -0
  44. package/blueprints/features/db-drizzle-postgres/BLUEPRINT.md +596 -0
  45. package/blueprints/features/db-drizzle-postgres/files/db/drizzle.config.ts +18 -0
  46. package/blueprints/features/db-drizzle-postgres/files/db/env.example +3 -0
  47. package/blueprints/features/db-drizzle-postgres/files/db/package.json +33 -0
  48. package/blueprints/features/db-drizzle-postgres/files/db/src/client.ts +34 -0
  49. package/blueprints/features/db-drizzle-postgres/files/db/src/example-entity.ts +73 -0
  50. package/blueprints/features/db-drizzle-postgres/files/db/src/index.ts +8 -0
  51. package/blueprints/features/db-drizzle-postgres/files/db/src/scripts/seed.ts +50 -0
  52. package/blueprints/features/db-drizzle-postgres/files/db/tsconfig.json +13 -0
  53. package/blueprints/features/tailwind-v4/BLUEPRINT.md +29 -0
  54. package/blueprints/features/ui-shared-components/BLUEPRINT.md +35 -0
  55. package/blueprints/foundation/monorepo-turbo/BLUEPRINT.md +378 -0
  56. package/blueprints/foundation/monorepo-turbo/files/apps/web/app/globals.css +2 -0
  57. package/blueprints/foundation/monorepo-turbo/files/apps/web/app/layout.tsx +23 -0
  58. package/blueprints/foundation/monorepo-turbo/files/apps/web/app/page.tsx +7 -0
  59. package/blueprints/foundation/monorepo-turbo/files/apps/web/env.ts +13 -0
  60. package/blueprints/foundation/monorepo-turbo/files/apps/web/next.config.ts +12 -0
  61. package/blueprints/foundation/monorepo-turbo/files/apps/web/package.json +28 -0
  62. package/blueprints/foundation/monorepo-turbo/files/apps/web/postcss.config.mjs +5 -0
  63. package/blueprints/foundation/monorepo-turbo/files/apps/web/tsconfig.json +18 -0
  64. package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/package.json +14 -0
  65. package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/postcss.config.mjs +5 -0
  66. package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/shared-styles.css +88 -0
  67. package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/base.json +19 -0
  68. package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/nextjs.json +12 -0
  69. package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/package.json +5 -0
  70. package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/react-library.json +7 -0
  71. package/blueprints/foundation/monorepo-turbo/files/root/biome.json +46 -0
  72. package/blueprints/foundation/monorepo-turbo/files/root/gitignore +35 -0
  73. package/blueprints/foundation/monorepo-turbo/files/root/package.json +25 -0
  74. package/blueprints/foundation/monorepo-turbo/files/root/pnpm-workspace.yaml +5 -0
  75. package/blueprints/foundation/monorepo-turbo/files/root/turbo.json +30 -0
  76. package/dist/index.js +453 -0
  77. package/package.json +53 -0
@@ -0,0 +1,596 @@
1
+ # Database Blueprint — Drizzle ORM + PostgreSQL
2
+
3
+ ## Tier
4
+
5
+ Feature
6
+
7
+ ## Status
8
+
9
+ Complete.
10
+
11
+ ## Problem
12
+
13
+ Setting up a structured database layer involves connection management, schema organization, migration workflows, and clean imports for consuming code. Getting the Drizzle Kit config, migration scripts, schema group organization, and project wiring right from scratch is error-prone and time-consuming.
14
+
15
+ This blueprint creates a centralized database layer (`packages/db/` in monorepos, `src/db/` in single-repo apps) with opinionated patterns for schema organization, migration workflows, and clean consumption.
16
+
17
+ ## Blueprint Dependencies
18
+
19
+ None. This blueprint is standalone and can be installed into any project.
20
+
21
+ ### Interaction with auth-better-auth
22
+
23
+ If auth-better-auth was installed first, the database directory will already exist with an `auth` schema group. This blueprint merges into the existing structure — it does not overwrite auth files. See "Interaction with Auth Blueprint" section for details.
24
+
25
+ ---
26
+
27
+ ## Architecture
28
+
29
+ ### Core Pattern
30
+
31
+ All database schemas live in a single shared package (`packages/db/`), organized into **schema groups** — subdirectories under `src/` that each represent a logical domain or database. Each group has its own Drizzle Kit config for independent migration generation, its own barrel export, and its own subpath export in `package.json`.
32
+
33
+ Consuming packages import schemas via subpath exports (`@scope/db/core`) and create Drizzle client instances with `drizzle(new pg.Pool({...}), { schema })`.
34
+
35
+ ### Key Decisions
36
+
37
+ - **One package, multiple schema groups** (required) — All database schemas live in `packages/db/`. Groups are subdirectories under `src/` (e.g., `src/core/`, `src/auth/`). This keeps all database-related code centralized while supporting multiple databases.
38
+
39
+ - **Per-group drizzle.config.ts** (required) — Each group has its own config at `drizzle/<group>/drizzle.config.ts`. This supports separate databases per group (multi-db pattern) and avoids migration conflicts between unrelated schemas.
40
+
41
+ - **Explicit schema file lists** (required) — `drizzle.config.ts` lists each schema file individually, NOT via globs or directory paths. Excludes `index.ts` barrel files to avoid import issues during `drizzle-kit generate`. When you add a new entity file, you must add it to the config's `schema` array.
42
+
43
+ - **One file per domain entity** (required) — Each table gets its own file: enums at top, table definition, indexes, type exports at bottom. Follows ENGINEERING.md "feature-based structure".
44
+
45
+ - **Barrel exports per schema group** (required) — Each group has `index.ts` that re-exports all entity files. Consuming packages import from the barrel: `import { users, articles } from "@scope/db/core"`.
46
+
47
+ - **Subpath exports** (required) — `package.json` uses `"exports": { "./<group>": ... }` for clean cross-package imports. Each group gets its own entry point.
48
+
49
+ - **Committed migrations** (required) — Generated SQL migration files live in `drizzle/<group>/` alongside the config and are committed to version control. This provides a clear audit trail of schema changes.
50
+
51
+ - **UUID primary keys** (recommended) — Application tables use `uuid("id").primaryKey().defaultRandom()`. Auth tables use text IDs (generated by Better Auth). Configurable per project.
52
+
53
+ - **Automatic timestamps** (recommended) — Every table includes `createdAt: timestamp().defaultNow()` and `updatedAt: timestamp().defaultNow().$onUpdate(() => new Date())`.
54
+
55
+ - **Single database default** (recommended) — Blueprint starts with one database and one schema group. The multi-db pattern (separate `DATABASE_URL` per group) is documented and easy to adopt later.
56
+
57
+ ### Connection Patterns
58
+
59
+ Two approaches for creating Drizzle client instances:
60
+
61
+ **Option A — Client helper in db package (blueprint default):**
62
+ ```typescript
63
+ // packages/db/src/core/client.ts
64
+ import * as schema from "./index.js";
65
+ const pool = new pg.Pool({ connectionString: env.CORE_DATABASE_URL });
66
+ export const db = drizzle(pool, { schema });
67
+ ```
68
+ Simpler. One connection per group. Good for single-server architectures. Consuming packages import `db` directly.
69
+
70
+ **Option B — Client in each consuming app:**
71
+ ```typescript
72
+ // apis/server/src/db/core.ts
73
+ import * as schema from "@scope/db/core";
74
+ const pool = new pg.Pool({ connectionString: env.CORE_DATABASE_URL });
75
+ export const db = drizzle(pool, { schema });
76
+ ```
77
+ Each app controls its own pool configuration (size, timeout). Better for multi-server setups. The auth blueprint uses this pattern.
78
+
79
+ The blueprint generates Option A by default. Delete `client.ts` and adopt Option B if your architecture requires per-app pool control.
80
+
81
+ ---
82
+
83
+ ## Hard Requirements vs Recommended Defaults
84
+
85
+ **Hard requirements:**
86
+ - Drizzle ORM as the database library
87
+ - PostgreSQL as the database
88
+ - Centralized db directory (`packages/db/` in monorepo, `src/db/` in single-repo)
89
+
90
+ **Recommended defaults — ask during scaffolding:**
91
+
92
+ | Choice | Recommended | Alternatives |
93
+ |---|---|---|
94
+ | Schema group name | `"core"` | Any name: `"app"`, `"main"`, etc. |
95
+ | Primary key type | UUID (`defaultRandom()`) | serial, CUID, ULID, text |
96
+ | Include example entity | Yes | No (empty scaffold) |
97
+ | Include seed script | Yes | No |
98
+ | Include client helper | Yes (in db package) | No (per-app connections) |
99
+ | Database per group | Single database (shared URL) | Separate database per group |
100
+
101
+ ---
102
+
103
+ ## Dependencies
104
+
105
+ ### npm packages
106
+
107
+ **packages/db:**
108
+ | Package | Version | Purpose |
109
+ |---|---|---|
110
+ | `drizzle-orm` | ^0.45.1 | Database ORM |
111
+ | `dotenv` | ^16.4.5 | Env loading for drizzle configs and scripts |
112
+
113
+ **packages/db (dev):**
114
+ | Package | Version | Purpose |
115
+ |---|---|---|
116
+ | `drizzle-kit` | ^0.31.8 | Migration generation, execution, and Drizzle Studio |
117
+ | `pg` | ^8.18.0 | PostgreSQL driver (node-postgres) |
118
+ | `@types/pg` | ^8.16.0 | Type definitions for pg |
119
+ | `tsx` | ^4 | Running seed scripts directly from TypeScript |
120
+ | `typescript` | ^5.9 | TypeScript compiler |
121
+
122
+ ### Environment Variables
123
+
124
+ | Variable | Where | Description |
125
+ |---|---|---|
126
+ | `{{GROUP_UPPER}}_DATABASE_URL` | `packages/db/.env` | PostgreSQL connection URL (e.g., `CORE_DATABASE_URL`) |
127
+
128
+ ---
129
+
130
+ ## Monorepo Wiring
131
+
132
+ ### DB Package (`packages/db/`)
133
+
134
+ **`package.json` exports:**
135
+ ```json
136
+ {
137
+ "exports": {
138
+ "./{{GROUP}}": {
139
+ "import": "./dist/{{GROUP}}/index.js",
140
+ "types": "./dist/{{GROUP}}/index.d.ts"
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
146
+ **Scripts:**
147
+ ```json
148
+ {
149
+ "build": "tsc -p tsconfig.json",
150
+ "typecheck": "tsc -p tsconfig.json --noEmit",
151
+ "clean": "rm -rf dist",
152
+ "{{GROUP}}:generate": "drizzle-kit generate --config ./drizzle/{{GROUP}}/drizzle.config.ts",
153
+ "{{GROUP}}:migrate": "drizzle-kit generate --config ./drizzle/{{GROUP}}/drizzle.config.ts && drizzle-kit migrate --config ./drizzle/{{GROUP}}/drizzle.config.ts",
154
+ "drizzle:studio:{{GROUP}}": "drizzle-kit studio --config ./drizzle/{{GROUP}}/drizzle.config.ts",
155
+ "seed": "tsx src/scripts/seed.ts"
156
+ }
157
+ ```
158
+
159
+ **File structure:**
160
+ ```
161
+ packages/db/
162
+ ├── package.json
163
+ ├── tsconfig.json
164
+ ├── .env # DATABASE_URL(s)
165
+ ├── .env.example
166
+ ├── drizzle/
167
+ │ └── {{GROUP}}/
168
+ │ ├── drizzle.config.ts # Drizzle Kit config
169
+ │ ├── *.sql # Generated migrations (committed)
170
+ │ └── meta/ # Drizzle Kit snapshots (committed)
171
+ └── src/
172
+ ├── {{GROUP}}/
173
+ │ ├── index.ts # Barrel export
174
+ │ ├── client.ts # Connection helper (optional)
175
+ │ └── example.ts # Example entity (optional)
176
+ └── scripts/
177
+ └── seed.ts # Seed script (optional)
178
+ ```
179
+
180
+ ### Root `package.json`
181
+
182
+ ```json
183
+ {
184
+ "scripts": {
185
+ "db:generate": "turbo run {{GROUP}}:generate --filter={{SCOPE}}/db --no-cache",
186
+ "db:migrate": "turbo run {{GROUP}}:migrate --filter={{SCOPE}}/db --no-cache",
187
+ "db:studio": "turbo run drizzle:studio:{{GROUP}} --filter={{SCOPE}}/db --no-cache",
188
+ "db:seed": "turbo run seed --filter={{SCOPE}}/db --no-cache"
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### `turbo.json`
194
+
195
+ ```json
196
+ {
197
+ "tasks": {
198
+ "{{GROUP}}:generate": { "cache": false },
199
+ "{{GROUP}}:migrate": { "cache": false }
200
+ }
201
+ }
202
+ ```
203
+
204
+ ### Consuming App Pattern
205
+
206
+ ```typescript
207
+ // Option A: Using the client helper
208
+ import { db } from "{{SCOPE}}/db/{{GROUP}}/client";
209
+
210
+ const users = await db.query.example.findMany();
211
+
212
+ // Option B: Managing your own connection
213
+ import * as schema from "{{SCOPE}}/db/{{GROUP}}";
214
+ import { drizzle } from "drizzle-orm/node-postgres";
215
+ import pg from "pg";
216
+
217
+ const pool = new pg.Pool({ connectionString: env.DATABASE_URL });
218
+ export const db = drizzle(pool, { schema });
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Single-Repo Adaptation
224
+
225
+ For standalone applications (Next.js, Node, Bun) without a monorepo, the core database patterns are identical — only file placement and wiring differ.
226
+
227
+ ### Path Mapping
228
+
229
+ | Aspect | Monorepo | Single-Repo |
230
+ |--------|----------|-------------|
231
+ | DB code location | `packages/db/` (own package.json) | `src/db/` (directory in app) |
232
+ | Import pattern | `import { x } from "@scope/db/core"` | `import { x } from "@/db/core"` or `#db/core` |
233
+ | Package dependencies | `workspace:*` protocol | Direct — packages at root `package.json` |
234
+ | TypeScript config | Extends `@scope/typescript-config/base.json` | Inherits from root `tsconfig.json` |
235
+ | Drizzle config location | `packages/db/drizzle/<group>/drizzle.config.ts` | `drizzle/<group>/drizzle.config.ts` (project root) |
236
+ | Schema paths in config | `./src/<group>/example.ts` (relative to `packages/db/`) | `./src/db/<group>/example.ts` (relative to root) |
237
+ | `.env` location | `packages/db/.env` | `.env` or `.env.local` at project root |
238
+ | Build step | Separate `tsc` in db package | Framework transpiles directly (no separate build) |
239
+ | Scripts | Turbo wrappers in root package.json | Direct drizzle-kit calls in root package.json |
240
+ | Task orchestration | Turbo tasks in `turbo.json` (`cache: false`) | N/A — no Turbo |
241
+
242
+ ### Single-Repo Scripts
243
+
244
+ ```json
245
+ {
246
+ "scripts": {
247
+ "db:generate": "drizzle-kit generate --config ./drizzle/<group>/drizzle.config.ts",
248
+ "db:migrate": "drizzle-kit generate --config ./drizzle/<group>/drizzle.config.ts && drizzle-kit migrate --config ./drizzle/<group>/drizzle.config.ts",
249
+ "db:studio": "drizzle-kit studio --config ./drizzle/<group>/drizzle.config.ts",
250
+ "db:seed": "tsx src/db/scripts/seed.ts"
251
+ }
252
+ }
253
+ ```
254
+
255
+ ### Single-Repo File Structure
256
+
257
+ ```
258
+ <project-root>/
259
+ ├── src/db/
260
+ │ ├── <group>/
261
+ │ │ ├── index.ts # Barrel export
262
+ │ │ ├── client.ts # Connection helper
263
+ │ │ └── example.ts # Example entity
264
+ │ └── scripts/
265
+ │ └── seed.ts # Seed script
266
+ ├── drizzle/
267
+ │ └── <group>/
268
+ │ ├── drizzle.config.ts # Drizzle Kit config
269
+ │ ├── *.sql # Generated migrations
270
+ │ └── meta/ # Drizzle Kit snapshots
271
+ ├── .env # DATABASE_URL(s)
272
+ └── package.json # Scripts + dependencies at root
273
+ ```
274
+
275
+ ### What Stays the Same
276
+
277
+ All core patterns are project-type-agnostic:
278
+ - Drizzle ORM + PostgreSQL
279
+ - Schema groups (directories for domain separation)
280
+ - Per-group `drizzle.config.ts` with explicit file lists
281
+ - Entity file pattern (pgTable, indexes, type exports, one file per entity)
282
+ - Barrel exports per group (`index.ts`)
283
+ - Migration workflow (generate → migrate), committed migrations
284
+ - Seed scripts, connection helpers, `.env` pattern
285
+
286
+ ---
287
+
288
+ ## Interaction with Auth Blueprint
289
+
290
+ The auth blueprint (`auth-better-auth`) also creates database schemas in `packages/db/`. Here's how the two blueprints interact:
291
+
292
+ ### Scenario A: DB blueprint first, auth second (recommended)
293
+
294
+ 1. DB blueprint creates `packages/db/` with the initial schema group (e.g., `core`)
295
+ 2. Auth blueprint detects the existing `packages/db/` and adds to it:
296
+ - Adds `"./auth"` export to `package.json`
297
+ - Adds `auth:generate`, `auth:migrate`, `drizzle:studio:auth` scripts
298
+ - Creates `src/auth/schema.ts` and `drizzle/auth/drizzle.config.ts`
299
+ - Does NOT overwrite existing files
300
+
301
+ ### Scenario B: Auth blueprint first, db second
302
+
303
+ 1. Auth blueprint creates a minimal `packages/db/` with just the auth group
304
+ 2. DB blueprint detects the existing package and merges:
305
+ - Adds the new group's export alongside `"./auth"`
306
+ - Adds new scripts without overwriting auth scripts
307
+ - Creates new group directory structure alongside `src/auth/`
308
+ - Skips `tsconfig.json` if already present
309
+
310
+ ### Shared vs Separate Databases
311
+
312
+ - **Separate databases** (recommended for auth): Auth and core use different `DATABASE_URL` env vars (`AUTH_DATABASE_URL`, `CORE_DATABASE_URL`). Each group has independent migrations.
313
+ - **Shared database**: Both auth and core schemas point to the same `DATABASE_URL`. Simpler but couples auth and application data.
314
+
315
+ ---
316
+
317
+ ## Schema File Patterns
318
+
319
+ ### Entity File Structure
320
+
321
+ Every entity file follows this pattern:
322
+
323
+ ```typescript
324
+ // 1. Imports
325
+ import { index, pgEnum, pgTable, text, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
326
+
327
+ // 2. Enums (optional)
328
+ export const statusEnum = pgEnum("entity_status", ["active", "archived"]);
329
+
330
+ // 3. Table definition
331
+ export const entity = pgTable(
332
+ "entity",
333
+ {
334
+ id: uuid("id").primaryKey().defaultRandom(),
335
+ name: varchar("name", { length: 200 }).notNull(),
336
+ status: statusEnum("status").default("active").notNull(),
337
+ createdAt: timestamp("created_at").defaultNow().notNull(),
338
+ updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => new Date()).notNull(),
339
+ },
340
+ (table) => [
341
+ index("entity_name_idx").on(table.name),
342
+ index("entity_status_idx").on(table.status),
343
+ ],
344
+ );
345
+
346
+ // 4. Type exports
347
+ export type Entity = typeof entity.$inferSelect;
348
+ export type NewEntity = typeof entity.$inferInsert;
349
+ ```
350
+
351
+ ### Common Column Patterns
352
+
353
+ ```typescript
354
+ // UUID primary key
355
+ id: uuid("id").primaryKey().defaultRandom(),
356
+
357
+ // Text primary key (for external IDs like Better Auth)
358
+ id: text("id").primaryKey(),
359
+
360
+ // Foreign key to user (text, matches Better Auth user.id)
361
+ userId: text("user_id").notNull(),
362
+
363
+ // Foreign key to another table in same group
364
+ categoryId: uuid("category_id").references(() => category.id, { onDelete: "cascade" }),
365
+
366
+ // PostgreSQL enum
367
+ status: statusEnum("status").default("active").notNull(),
368
+
369
+ // JSONB field with TypeScript type
370
+ metadata: jsonb("metadata").$type<{ key: string; value: unknown }>(),
371
+
372
+ // Array field
373
+ tags: text("tags").array(),
374
+
375
+ // Automatic timestamps
376
+ createdAt: timestamp("created_at").defaultNow().notNull(),
377
+ updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => new Date()).notNull(),
378
+
379
+ // Soft delete
380
+ deletedAt: timestamp("deleted_at"),
381
+ ```
382
+
383
+ ### Index Patterns
384
+
385
+ ```typescript
386
+ // Single column index
387
+ index("entity_user_id_idx").on(table.userId),
388
+
389
+ // Composite index
390
+ index("entity_org_status_idx").on(table.organizationId, table.status),
391
+
392
+ // Unique constraint
393
+ uniqueIndex("entity_slug_unique").on(table.slug),
394
+ ```
395
+
396
+ ---
397
+
398
+ ## File Manifest
399
+
400
+ | File | Purpose | Monorepo Target | Single-Repo Target |
401
+ |---|---|---|---|
402
+ | `files/db/package.json` | Package config with exports, scripts, deps | `packages/db/package.json` | N/A (scripts go in root `package.json`) |
403
+ | `files/db/tsconfig.json` | TypeScript config extending shared base | `packages/db/tsconfig.json` | N/A (use root `tsconfig.json`) |
404
+ | `files/db/env.example` | Example env with DATABASE_URL | `packages/db/.env.example` | `.env.example` (merge into root) |
405
+ | `files/db/drizzle.config.ts` | Per-group Drizzle Kit config template | `packages/db/drizzle/{{GROUP}}/drizzle.config.ts` | `drizzle/{{GROUP}}/drizzle.config.ts` |
406
+ | `files/db/src/index.ts` | Barrel export for schema group | `packages/db/src/{{GROUP}}/index.ts` | `src/db/{{GROUP}}/index.ts` |
407
+ | `files/db/src/example-entity.ts` | Example entity showing full pattern | `packages/db/src/{{GROUP}}/example.ts` | `src/db/{{GROUP}}/example.ts` |
408
+ | `files/db/src/client.ts` | Optional connection helper | `packages/db/src/{{GROUP}}/client.ts` | `src/db/{{GROUP}}/client.ts` |
409
+ | `files/db/src/scripts/seed.ts` | Example seed script using tsx | `packages/db/src/scripts/seed.ts` | `src/db/scripts/seed.ts` |
410
+
411
+ ---
412
+
413
+ ## Integration Steps
414
+
415
+ ### Phase 1: Package Setup
416
+
417
+ 1. Create `packages/db/` directory
418
+ 2. Copy `package.json` — replace `{{SCOPE}}` and `{{GROUP}}` placeholders
419
+ 3. Copy `tsconfig.json` — replace `{{SCOPE}}` with shared config package name
420
+ 4. Copy `.env.example` — replace `{{GROUP_UPPER}}`
421
+ 5. Create `packages/db/.env` from `.env.example` with actual DATABASE_URL
422
+
423
+ ### Phase 2: Schema Group
424
+
425
+ 6. Create `src/{{GROUP}}/` directory
426
+ 7. Copy example entity file (if included) — demonstrates full pattern
427
+ 8. Create barrel `index.ts` with re-exports
428
+ 9. Copy `client.ts` helper (if included) — replace `{{SCOPE}}`, `{{GROUP}}`, `{{GROUP_UPPER}}`
429
+
430
+ ### Phase 3: Drizzle Config
431
+
432
+ 10. Create `drizzle/{{GROUP}}/` directory
433
+ 11. Copy `drizzle.config.ts` — replace `{{GROUP}}`, `{{GROUP_UPPER}}`, set schema file paths
434
+
435
+ ### Phase 4: Seed Script (optional)
436
+
437
+ 12. Create `src/scripts/` directory
438
+ 13. Copy `seed.ts` — replace `{{GROUP}}`, `{{GROUP_UPPER}}`, adjust entity imports
439
+
440
+ ### Phase 5: Project Integration (monorepo)
441
+
442
+ > Skip this phase for single-repo projects — use Phase 5-alt instead.
443
+
444
+ 14. Add `{{GROUP}}:generate`, `{{GROUP}}:migrate` tasks to `turbo.json` (both `cache: false`)
445
+ 15. Add `db:generate`, `db:migrate`, `db:studio`, `db:seed` scripts to root `package.json` (Turbo wrappers)
446
+ 16. Verify `packages/db` is covered by `pnpm-workspace.yaml` (should be via `packages/*`)
447
+ 17. Run `pnpm install` to wire workspace dependencies
448
+
449
+ ### Phase 5-alt: Project Integration (single-repo)
450
+
451
+ > Use this phase instead of Phase 5 for standalone applications.
452
+
453
+ 14. Add `db:generate`, `db:migrate`, `db:studio`, `db:seed` scripts to root `package.json` (direct drizzle-kit calls — see Single-Repo Scripts above)
454
+ 15. Add drizzle-orm, dotenv as dependencies and drizzle-kit, pg, @types/pg, tsx as devDependencies in root `package.json`
455
+ 16. Set `{{GROUP_UPPER}}_DATABASE_URL` in `.env` at project root
456
+
457
+ ### Phase 6: Verification
458
+
459
+ 18. Set `{{GROUP_UPPER}}_DATABASE_URL` in `packages/db/.env`
460
+ 19. Run `pnpm db:generate` — should produce initial migration SQL
461
+ 20. Run `pnpm db:migrate` — should apply migration to database
462
+ 21. Run `pnpm db:studio` — should open Drizzle Studio
463
+ 22. Run `pnpm db:seed` — should insert sample data (if seed included)
464
+
465
+ ---
466
+
467
+ ## Adding New Entities
468
+
469
+ After initial scaffolding, add new domain entities with this checklist:
470
+
471
+ 1. Create `src/{{GROUP}}/new-entity.ts` (copy the example entity pattern)
472
+ 2. Add `export * from "./new-entity.js";` to `src/{{GROUP}}/index.ts`
473
+ 3. Add `"./src/{{GROUP}}/new-entity.ts"` to `drizzle/{{GROUP}}/drizzle.config.ts` schema array
474
+ 4. Run `pnpm {{GROUP}}:generate` to create the migration
475
+ 5. Run `pnpm {{GROUP}}:migrate` to apply it
476
+
477
+ ## Adding a New Schema Group
478
+
479
+ To add a second group (e.g., `"pipeline"`) for a separate database or logical domain:
480
+
481
+ 1. Create `src/pipeline/` directory with entity files and `index.ts`
482
+ 2. Create `drizzle/pipeline/drizzle.config.ts` (copy from existing group, update paths and env var)
483
+ 3. Add `"./pipeline"` export to `package.json`:
484
+ ```json
485
+ "./pipeline": {
486
+ "import": "./dist/pipeline/index.js",
487
+ "types": "./dist/pipeline/index.d.ts"
488
+ }
489
+ ```
490
+ 4. Add scripts: `pipeline:generate`, `pipeline:migrate`, `drizzle:studio:pipeline`
491
+ 5. Add `PIPELINE_DATABASE_URL` to `.env` and `.env.example`
492
+ 6. Add `pipeline:generate` and `pipeline:migrate` tasks to `turbo.json` (`cache: false`)
493
+ 7. (Optional) Add `db:generate:pipeline`, `db:migrate:pipeline` convenience scripts to root `package.json`
494
+
495
+ ---
496
+
497
+ ## Maintenance Hooks
498
+
499
+ ### Schema & Migration Hooks
500
+
501
+ | When you... | Then run... | Why |
502
+ |---|---|---|
503
+ | Add a new entity file | Add to `drizzle.config.ts` schema array AND `index.ts` barrel | Missing entries = invisible to migrations and consumers |
504
+ | Modify any schema file | `pnpm {{GROUP}}:generate` then `pnpm {{GROUP}}:migrate` | Schema changes need migrations generated and applied |
505
+ | Add a new schema group | Follow "Adding a New Schema Group" checklist | Multiple configs + exports + scripts needed |
506
+ | Update drizzle-orm or drizzle-kit | `pnpm {{GROUP}}:generate` and check for unexpected diffs | New versions may change migration format |
507
+
508
+ ### Environment Hooks
509
+
510
+ | When you... | Then do... | Why |
511
+ |---|---|---|
512
+ | Add a new `DATABASE_URL` | Add to `.env`, `.env.example`, and consuming app env validation | Missing URL = crash at startup |
513
+ | Change `DATABASE_URL` | Verify connection with `pnpm drizzle:studio:{{GROUP}}` | Wrong URL = silent connection failures |
514
+
515
+ ### Condensed Rules for CLAUDE.md
516
+
517
+ ```markdown
518
+ ### db-drizzle-postgres maintenance
519
+ - After creating a new schema file: add it to `drizzle/<group>/drizzle.config.ts` schema array AND the group's `index.ts` barrel export
520
+ - After any schema change: run `pnpm <group>:generate && pnpm <group>:migrate`
521
+ - After adding a new schema group: create drizzle config, add package.json export, add scripts (see BLUEPRINT.md "Adding a New Schema Group")
522
+ - After updating drizzle-orm/drizzle-kit: run generate and check migration diff for unexpected changes
523
+ - Never use glob patterns in drizzle.config.ts schema — always list files explicitly, excluding index.ts
524
+ ```
525
+
526
+ ---
527
+
528
+ ## What's Configurable
529
+
530
+ - Schema group name and number of groups
531
+ - Primary key strategy (UUID, serial, CUID, ULID, text)
532
+ - Whether to include example entity and seed script
533
+ - Connection helper location (in db package vs per consuming app)
534
+ - Single database vs separate database per group
535
+
536
+ ## What's Opinionated
537
+
538
+ - **Drizzle ORM** — the only supported ORM (hard requirement per ENGINEERING.md)
539
+ - **PostgreSQL** — the only supported database (this is `db-drizzle-*postgres*`)
540
+ - **Centralized db directory** — schemas live in one location (`packages/db/` in monorepo, `src/db/` in single-repo), not scattered across apps
541
+ - **Schema-per-file** — one file per domain entity, not one giant schema file
542
+ - **Per-group drizzle.config.ts** — independent migration generation per schema group
543
+ - **Explicit schema file lists** — no globs in drizzle config (avoids barrel export issues)
544
+ - **Committed migrations** — SQL files are version-controlled for auditability
545
+ - **`pg` (node-postgres) driver** — standard PostgreSQL driver for Node.js
546
+
547
+ ## Project Context Output
548
+
549
+ Appends to `.project-context.md` under `## Installed Blueprints`:
550
+
551
+ ```yaml
552
+ ### db-drizzle-postgres
553
+ blueprint: db-drizzle-postgres
554
+ choices:
555
+ schema_group: {{GROUP}}
556
+ include_example: true|false
557
+ include_seed: true|false
558
+ connection_helper: in-db-package|per-app
559
+ files_created:
560
+ - packages/db/package.json
561
+ - packages/db/tsconfig.json
562
+ - packages/db/.env.example
563
+ - packages/db/drizzle/{{GROUP}}/drizzle.config.ts
564
+ - packages/db/src/{{GROUP}}/index.ts
565
+ - packages/db/src/{{GROUP}}/example.ts (if included)
566
+ - packages/db/src/{{GROUP}}/client.ts (if included)
567
+ - packages/db/src/scripts/seed.ts (if included)
568
+ env_vars_added:
569
+ - {{GROUP_UPPER}}_DATABASE_URL
570
+ scripts_added:
571
+ db_package:
572
+ - {{GROUP}}:generate
573
+ - {{GROUP}}:migrate
574
+ - drizzle:studio:{{GROUP}}
575
+ - seed
576
+ root:
577
+ - db:generate
578
+ - db:migrate
579
+ - db:studio
580
+ - db:seed
581
+ turbo_tasks:
582
+ - {{GROUP}}:generate
583
+ - {{GROUP}}:migrate
584
+ ```
585
+
586
+ ---
587
+
588
+ ## References
589
+
590
+ - **Drizzle ORM Docs:** https://orm.drizzle.team/docs/overview
591
+ - **Drizzle PostgreSQL Guide:** https://orm.drizzle.team/docs/get-started/postgresql-new
592
+ - **Drizzle Kit CLI:** https://orm.drizzle.team/docs/kit-overview
593
+ - **Drizzle Schema Declaration:** https://orm.drizzle.team/docs/sql-schema-declaration
594
+ - **Drizzle Migrations:** https://orm.drizzle.team/docs/migrations
595
+ - **Drizzle Studio:** https://orm.drizzle.team/docs/drizzle-studio
596
+ - **node-postgres:** https://node-postgres.com/
@@ -0,0 +1,18 @@
1
+ import { config } from "dotenv";
2
+ import { defineConfig } from "drizzle-kit";
3
+
4
+ config({ path: "./.env" });
5
+
6
+ // CONFIGURE: Update this env var name if your group uses a different convention
7
+ const DATABASE_URL = process.env.{{GROUP_UPPER}}_DATABASE_URL!;
8
+
9
+ export default defineConfig({
10
+ // List each schema file explicitly — do NOT include index.ts or client.ts
11
+ // CONFIGURE: Paths are relative to where drizzle-kit runs. Add new schema files here as you create them
12
+ schema: ["./src/{{GROUP}}/example.ts"],
13
+ out: "./drizzle/{{GROUP}}",
14
+ dialect: "postgresql",
15
+ dbCredentials: {
16
+ url: DATABASE_URL,
17
+ },
18
+ });
@@ -0,0 +1,3 @@
1
+ # {{GROUP_UPPER}}_DATABASE_URL — PostgreSQL connection URL for the {{GROUP}} database
2
+ # Format: postgresql://user:password@host:port/database
3
+ {{GROUP_UPPER}}_DATABASE_URL=postgresql://user:password@localhost:5432/{{GROUP}}_db
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "{{SCOPE}}/db",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ "./{{GROUP}}": {
8
+ "import": "./dist/{{GROUP}}/index.js",
9
+ "types": "./dist/{{GROUP}}/index.d.ts"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "build": "tsc -p tsconfig.json",
14
+ "typecheck": "tsc -p tsconfig.json --noEmit",
15
+ "clean": "rm -rf dist",
16
+ "{{GROUP}}:generate": "drizzle-kit generate --config ./drizzle/{{GROUP}}/drizzle.config.ts",
17
+ "{{GROUP}}:migrate": "drizzle-kit generate --config ./drizzle/{{GROUP}}/drizzle.config.ts && drizzle-kit migrate --config ./drizzle/{{GROUP}}/drizzle.config.ts",
18
+ "drizzle:studio:{{GROUP}}": "drizzle-kit studio --config ./drizzle/{{GROUP}}/drizzle.config.ts",
19
+ "seed": "tsx src/scripts/seed.ts"
20
+ },
21
+ "dependencies": {
22
+ "dotenv": "^16.4.5",
23
+ "drizzle-orm": "^0.45.1"
24
+ },
25
+ "devDependencies": {
26
+ "{{SCOPE}}/typescript-config": "workspace:*",
27
+ "@types/pg": "^8.16.0",
28
+ "drizzle-kit": "^0.31.8",
29
+ "pg": "^8.18.0",
30
+ "tsx": "^4.19.0",
31
+ "typescript": "^5.9.0"
32
+ }
33
+ }
@@ -0,0 +1,34 @@
1
+ // =============================================================================
2
+ // {{GROUP}} Database Client
3
+ // =============================================================================
4
+ // Provides a ready-to-use Drizzle instance connected to the {{GROUP}} database.
5
+ //
6
+ // Usage in consuming packages:
7
+ // import { db } from "{{SCOPE}}/db/{{GROUP}}/client";
8
+ // const rows = await db.query.example.findMany();
9
+ //
10
+ // CONFIGURE: If your app needs custom pool settings per environment (different
11
+ // pool sizes, timeouts, SSL config), consider moving connection setup into each
12
+ // consuming app instead. See BLUEPRINT.md "Connection Patterns" for details.
13
+ // =============================================================================
14
+
15
+ import { config } from "dotenv";
16
+ import { drizzle } from "drizzle-orm/node-postgres";
17
+ import pg from "pg";
18
+ import * as schema from "./index.js";
19
+
20
+ config({ path: "./.env" });
21
+
22
+ const DATABASE_URL = process.env.{{GROUP_UPPER}}_DATABASE_URL;
23
+ if (!DATABASE_URL) {
24
+ throw new Error(
25
+ // CONFIGURE: Adjust path reference for your project structure
26
+ "Missing {{GROUP_UPPER}}_DATABASE_URL — set it in your .env file",
27
+ );
28
+ }
29
+
30
+ const pool = new pg.Pool({ connectionString: DATABASE_URL });
31
+
32
+ export const db = drizzle(pool, { schema });
33
+
34
+ export type DbClient = typeof db;