@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.
- package/.claude/commands/audit.md +179 -0
- package/.claude/commands/discover.md +92 -0
- package/.claude/commands/new-blueprint.md +265 -0
- package/.claude/commands/new-project.md +230 -0
- package/.claude/commands/scaffold-auth.md +310 -0
- package/.claude/commands/scaffold-db.md +270 -0
- package/.claude/commands/scaffold-foundation.md +158 -0
- package/.cursor/commands/audit.md +179 -0
- package/.cursor/commands/discover.md +92 -0
- package/.cursor/commands/new-blueprint.md +265 -0
- package/.cursor/commands/new-project.md +230 -0
- package/.cursor/commands/scaffold-auth.md +310 -0
- package/.cursor/commands/scaffold-db.md +270 -0
- package/.cursor/commands/scaffold-foundation.md +158 -0
- package/.opencode/commands/audit.md +183 -0
- package/.opencode/commands/discover.md +96 -0
- package/.opencode/commands/new-blueprint.md +269 -0
- package/.opencode/commands/new-project.md +234 -0
- package/.opencode/commands/scaffold-auth.md +314 -0
- package/.opencode/commands/scaffold-db.md +274 -0
- package/.opencode/commands/scaffold-foundation.md +162 -0
- package/ENGINEERING.md +47 -0
- package/blueprints/discovery/product-discovery/BLUEPRINT.md +361 -0
- package/blueprints/discovery/product-discovery/templates/archetype-template.md +143 -0
- package/blueprints/discovery/product-discovery/templates/product-brief.md +65 -0
- package/blueprints/discovery/product-discovery/templates/product-thesis.md +64 -0
- package/blueprints/discovery/product-discovery/templates/research-summary.md +43 -0
- package/blueprints/features/auth-better-auth/BLUEPRINT.md +794 -0
- package/blueprints/features/auth-better-auth/files/client/auth-client.ts +31 -0
- package/blueprints/features/auth-better-auth/files/client/context/organization.ts +236 -0
- package/blueprints/features/auth-better-auth/files/client/hooks/use-create-organization.ts +45 -0
- package/blueprints/features/auth-better-auth/files/client/hooks/use-has-permission.ts +26 -0
- package/blueprints/features/auth-better-auth/files/client/hooks/use-organization.ts +64 -0
- package/blueprints/features/auth-better-auth/files/client/schema/auth.ts +21 -0
- package/blueprints/features/auth-better-auth/files/client/schema/organization.ts +51 -0
- package/blueprints/features/auth-better-auth/files/client/types/organization.ts +20 -0
- package/blueprints/features/auth-better-auth/files/db/auth-schema.ts +184 -0
- package/blueprints/features/auth-better-auth/files/db/drizzle.config.ts +21 -0
- package/blueprints/features/auth-better-auth/files/middleware/route-protection.ts +84 -0
- package/blueprints/features/auth-better-auth/files/server/auth-db.ts +18 -0
- package/blueprints/features/auth-better-auth/files/server/auth.ts +159 -0
- package/blueprints/features/auth-better-auth/files/server/env.ts +44 -0
- package/blueprints/features/auth-better-auth/files/server/middleware.ts +144 -0
- package/blueprints/features/db-drizzle-postgres/BLUEPRINT.md +596 -0
- package/blueprints/features/db-drizzle-postgres/files/db/drizzle.config.ts +18 -0
- package/blueprints/features/db-drizzle-postgres/files/db/env.example +3 -0
- package/blueprints/features/db-drizzle-postgres/files/db/package.json +33 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/client.ts +34 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/example-entity.ts +73 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/index.ts +8 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/scripts/seed.ts +50 -0
- package/blueprints/features/db-drizzle-postgres/files/db/tsconfig.json +13 -0
- package/blueprints/features/tailwind-v4/BLUEPRINT.md +29 -0
- package/blueprints/features/ui-shared-components/BLUEPRINT.md +35 -0
- package/blueprints/foundation/monorepo-turbo/BLUEPRINT.md +378 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/app/globals.css +2 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/app/layout.tsx +23 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/app/page.tsx +7 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/env.ts +13 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/next.config.ts +12 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/package.json +28 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/postcss.config.mjs +5 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/tsconfig.json +18 -0
- package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/package.json +14 -0
- package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/postcss.config.mjs +5 -0
- package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/shared-styles.css +88 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/base.json +19 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/nextjs.json +12 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/package.json +5 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/react-library.json +7 -0
- package/blueprints/foundation/monorepo-turbo/files/root/biome.json +46 -0
- package/blueprints/foundation/monorepo-turbo/files/root/gitignore +35 -0
- package/blueprints/foundation/monorepo-turbo/files/root/package.json +25 -0
- package/blueprints/foundation/monorepo-turbo/files/root/pnpm-workspace.yaml +5 -0
- package/blueprints/foundation/monorepo-turbo/files/root/turbo.json +30 -0
- package/dist/index.js +453 -0
- 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,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;
|