opencastle 0.33.9 → 0.34.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 (112) hide show
  1. package/dist/cli/init.d.ts.map +1 -1
  2. package/dist/cli/init.js +39 -17
  3. package/dist/cli/init.js.map +1 -1
  4. package/dist/cli/stack-config.d.ts.map +1 -1
  5. package/dist/cli/stack-config.js +5 -0
  6. package/dist/cli/stack-config.js.map +1 -1
  7. package/dist/cli/types.d.ts +1 -1
  8. package/dist/cli/types.d.ts.map +1 -1
  9. package/dist/orchestrator/plugins/cloudflare/config.d.ts +3 -0
  10. package/dist/orchestrator/plugins/cloudflare/config.d.ts.map +1 -0
  11. package/dist/orchestrator/plugins/cloudflare/config.js +23 -0
  12. package/dist/orchestrator/plugins/cloudflare/config.js.map +1 -0
  13. package/dist/orchestrator/plugins/coolify/config.d.ts +3 -0
  14. package/dist/orchestrator/plugins/coolify/config.d.ts.map +1 -0
  15. package/dist/orchestrator/plugins/coolify/config.js +28 -0
  16. package/dist/orchestrator/plugins/coolify/config.js.map +1 -0
  17. package/dist/orchestrator/plugins/drizzle/config.d.ts +3 -0
  18. package/dist/orchestrator/plugins/drizzle/config.d.ts.map +1 -0
  19. package/dist/orchestrator/plugins/drizzle/config.js +15 -0
  20. package/dist/orchestrator/plugins/drizzle/config.js.map +1 -0
  21. package/dist/orchestrator/plugins/expo/config.d.ts +3 -0
  22. package/dist/orchestrator/plugins/expo/config.d.ts.map +1 -0
  23. package/dist/orchestrator/plugins/expo/config.js +23 -0
  24. package/dist/orchestrator/plugins/expo/config.js.map +1 -0
  25. package/dist/orchestrator/plugins/index.d.ts.map +1 -1
  26. package/dist/orchestrator/plugins/index.js +12 -0
  27. package/dist/orchestrator/plugins/index.js.map +1 -1
  28. package/dist/orchestrator/plugins/sentry/config.d.ts +3 -0
  29. package/dist/orchestrator/plugins/sentry/config.d.ts.map +1 -0
  30. package/dist/orchestrator/plugins/sentry/config.js +28 -0
  31. package/dist/orchestrator/plugins/sentry/config.js.map +1 -0
  32. package/dist/orchestrator/plugins/stripe/config.d.ts +3 -0
  33. package/dist/orchestrator/plugins/stripe/config.d.ts.map +1 -0
  34. package/dist/orchestrator/plugins/stripe/config.js +42 -0
  35. package/dist/orchestrator/plugins/stripe/config.js.map +1 -0
  36. package/dist/orchestrator/plugins/types.d.ts +1 -1
  37. package/dist/orchestrator/plugins/types.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/src/cli/init.ts +43 -22
  40. package/src/cli/stack-config.ts +5 -0
  41. package/src/cli/types.ts +1 -1
  42. package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
  43. package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +4 -4
  44. package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +12 -12
  45. package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
  46. package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +1 -1
  47. package/src/dashboard/dist/data/convoys/demo-docs-update.json +3 -3
  48. package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
  49. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  50. package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
  51. package/src/dashboard/public/data/convoys/demo-auth-revamp.json +4 -4
  52. package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +12 -12
  53. package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
  54. package/src/dashboard/public/data/convoys/demo-deploy-ci.json +1 -1
  55. package/src/dashboard/public/data/convoys/demo-docs-update.json +3 -3
  56. package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
  57. package/src/orchestrator/customizations/agents/skill-matrix.json +24 -4
  58. package/src/orchestrator/customizations/agents/skill-matrix.md +5 -0
  59. package/src/orchestrator/plugins/cloudflare/SKILL.md +111 -0
  60. package/src/orchestrator/plugins/cloudflare/config.ts +24 -0
  61. package/src/orchestrator/plugins/cloudflare/references/deployment.md +147 -0
  62. package/src/orchestrator/plugins/cloudflare/references/storage.md +118 -0
  63. package/src/orchestrator/plugins/cloudflare/references/workers.md +135 -0
  64. package/src/orchestrator/plugins/convex/SKILL.md +62 -20
  65. package/src/orchestrator/plugins/convex/references/auth-auth0.md +116 -0
  66. package/src/orchestrator/plugins/convex/references/auth-clerk.md +113 -0
  67. package/src/orchestrator/plugins/convex/references/auth-convex-auth.md +143 -0
  68. package/src/orchestrator/plugins/convex/references/auth-setup.md +87 -0
  69. package/src/orchestrator/plugins/convex/references/auth-workos.md +114 -0
  70. package/src/orchestrator/plugins/convex/references/components-advanced.md +134 -0
  71. package/src/orchestrator/plugins/convex/references/components.md +171 -0
  72. package/src/orchestrator/plugins/convex/references/function-budget.md +232 -0
  73. package/src/orchestrator/plugins/convex/references/hot-path-rules.md +371 -0
  74. package/src/orchestrator/plugins/convex/references/migrations-component.md +170 -0
  75. package/src/orchestrator/plugins/convex/references/migrations.md +259 -0
  76. package/src/orchestrator/plugins/convex/references/occ-conflicts.md +126 -0
  77. package/src/orchestrator/plugins/convex/references/performance-audit.md +80 -0
  78. package/src/orchestrator/plugins/convex/references/quickstart.md +176 -0
  79. package/src/orchestrator/plugins/convex/references/subscription-cost.md +252 -0
  80. package/src/orchestrator/plugins/coolify/SKILL.md +134 -0
  81. package/src/orchestrator/plugins/coolify/config.ts +29 -0
  82. package/src/orchestrator/plugins/coolify/references/applications.md +65 -0
  83. package/src/orchestrator/plugins/coolify/references/ci-cd-webhooks.md +73 -0
  84. package/src/orchestrator/plugins/coolify/references/databases-services.md +57 -0
  85. package/src/orchestrator/plugins/coolify/references/docker-compose.md +121 -0
  86. package/src/orchestrator/plugins/coolify/references/infrastructure.md +77 -0
  87. package/src/orchestrator/plugins/drizzle/SKILL.md +123 -0
  88. package/src/orchestrator/plugins/drizzle/config.ts +16 -0
  89. package/src/orchestrator/plugins/drizzle/references/migrations.md +112 -0
  90. package/src/orchestrator/plugins/drizzle/references/query-patterns.md +127 -0
  91. package/src/orchestrator/plugins/drizzle/references/schema-patterns.md +105 -0
  92. package/src/orchestrator/plugins/expo/SKILL.md +114 -0
  93. package/src/orchestrator/plugins/expo/config.ts +24 -0
  94. package/src/orchestrator/plugins/expo/references/eas-build.md +73 -0
  95. package/src/orchestrator/plugins/expo/references/native-modules.md +71 -0
  96. package/src/orchestrator/plugins/expo/references/routing.md +83 -0
  97. package/src/orchestrator/plugins/index.ts +12 -0
  98. package/src/orchestrator/plugins/linear/SKILL.md +21 -3
  99. package/src/orchestrator/plugins/sentry/SKILL.md +94 -0
  100. package/src/orchestrator/plugins/sentry/config.ts +29 -0
  101. package/src/orchestrator/plugins/sentry/references/error-patterns.md +112 -0
  102. package/src/orchestrator/plugins/sentry/references/performance.md +66 -0
  103. package/src/orchestrator/plugins/sentry/references/sdk-setup.md +108 -0
  104. package/src/orchestrator/plugins/stripe/SKILL.md +138 -0
  105. package/src/orchestrator/plugins/stripe/config.ts +43 -0
  106. package/src/orchestrator/plugins/stripe/references/api-patterns.md +57 -0
  107. package/src/orchestrator/plugins/stripe/references/projects-setup.md +30 -0
  108. package/src/orchestrator/plugins/stripe/references/upgrade-guide.md +105 -0
  109. package/src/orchestrator/plugins/types.ts +1 -1
  110. package/src/orchestrator/skills/backbone-scaffolding/EXAMPLES.md +1 -1
  111. package/src/orchestrator/skills/backbone-scaffolding/SKILL.md +32 -16
  112. package/src/orchestrator/plugins/convex/REFERENCE.md +0 -9
@@ -0,0 +1,121 @@
1
+ # Docker Compose
2
+
3
+ ## Deployment Modes
4
+
5
+ | Mode | How | Constraints |
6
+ |------|-----|-------------|
7
+ | Raw (paste YAML) | Paste compose content directly | No `build:`, no external file mounts; use `image:` and `content:` for inline files |
8
+ | Repository | git URL | Full Docker Compose features: `build:`, external mounts, multi-file |
9
+
10
+ ## Magic Variables
11
+
12
+ Coolify auto-generates values for specially named env vars. Declare with port suffix, reference without.
13
+
14
+ | Type | Declaration Example | Generated Value |
15
+ |------|--------------------|-----------------|
16
+ | `PASSWORD` | `SERVICE_PASSWORD_DB` | Random password |
17
+ | `PASSWORD_64` | `SERVICE_PASSWORD_64_KEY` | 64-char password |
18
+ | `USER` | `SERVICE_USER_ADMIN` | Random 16-char string |
19
+ | `URL` | `SERVICE_URL_APP_3000` | `https://app-uuid.example.com` + Traefik routing |
20
+ | `FQDN` | `SERVICE_FQDN_APP` | `app-uuid.example.com` (no scheme) |
21
+
22
+ **Declaration vs. Reference:**
23
+ ```yaml
24
+ environment:
25
+ - SERVICE_URL_APP_3000 # Declare with port → activates proxy routing
26
+ - APP_URL=$SERVICE_URL_APP # Reference without port → injects https://… URL
27
+ ```
28
+
29
+ ⚠️ Use hyphens before port numbers: `SERVICE_URL_MY-SERVICE_3000` ✅ NOT `SERVICE_URL_MY_SERVICE_3000` ❌
30
+
31
+ **Shared credentials** — same `SERVICE_PASSWORD_*` identifier across services = same generated value:
32
+ ```yaml
33
+ services:
34
+ db: { environment: [SERVICE_PASSWORD_POSTGRES] }
35
+ app: { environment: [DB_PASS=$SERVICE_PASSWORD_POSTGRES] } # same value
36
+ ```
37
+
38
+ ## Env Var Syntax
39
+
40
+ ```yaml
41
+ environment:
42
+ - NODE_ENV=production # Hardcoded, hidden from UI
43
+ - API_KEY=${API_KEY} # Editable in UI (empty default)
44
+ - LOG_LEVEL=${LOG_LEVEL:-info} # Editable with default
45
+ - SECRET=${SECRET:?} # Required — blocks deploy if empty
46
+ ```
47
+
48
+ ## Health Check Patterns
49
+
50
+ ```yaml
51
+ healthcheck:
52
+ # HTTP
53
+ test: ["CMD-SHELL", "wget --spider -q http://localhost:8080"]
54
+ # PostgreSQL
55
+ test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
56
+ # MySQL
57
+ test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"]
58
+ # Redis
59
+ test: ["CMD-SHELL", "redis-cli ping"]
60
+ interval: 5s
61
+ timeout: 5s
62
+ retries: 10
63
+ start_period: 30s
64
+ ```
65
+
66
+ ## Coolify-Specific Extensions
67
+
68
+ ```yaml
69
+ volumes:
70
+ - /data/config:/app/config # standard bind mount
71
+ - source: /data/uploads
72
+ target: /app/uploads
73
+ is_directory: true # Coolify creates the directory if missing
74
+
75
+ - source: coolify-config
76
+ target: /app/config/app.conf
77
+ content: | # Inline file content (raw mode only)
78
+ log_level = info
79
+ bind = 0.0.0.0:8080
80
+ ```
81
+
82
+ ```yaml
83
+ # Exclude init/migration containers from health checks
84
+ services:
85
+ migrate:
86
+ image: myapp:latest
87
+ command: ["migrate", "up"]
88
+ labels:
89
+ - "exclude_from_hc=true"
90
+ ```
91
+
92
+ ## Header Metadata (for community templates)
93
+
94
+ ```yaml
95
+ # documentation: https://docs.example.com
96
+ # slogan: "One-click app name"
97
+ # category: self-hosted
98
+ # tags: Notes,Productivity
99
+ # logo: https://cdn.example.com/logo.png
100
+ # port: 3000
101
+ ```
102
+
103
+ ## Conversion Checklist
104
+
105
+ When converting a standard `docker-compose.yml` for Coolify:
106
+
107
+ 1. **Mode check** — `build:` directives → Raw mode: replace with `image:`; Repo mode: keep
108
+ 2. **Add header metadata** — `# documentation:`, `# slogan:`, `# category:`, `# tags:`, `# logo:`, `# port:`
109
+ 3. **Replace credentials** — hardcoded passwords/users → `SERVICE_PASSWORD_*` / `SERVICE_USER_*`
110
+ 4. **Replace URLs** — hardcoded URLs → `SERVICE_URL_*` variables
111
+ 5. **Remove `ports:`** — for proxied services (Traefik handles routing via `SERVICE_URL_*`)
112
+ 6. **Add health checks** — use patterns above; add `depends_on` with `condition: service_healthy`
113
+
114
+ ## Troubleshooting
115
+
116
+ | Symptom | Fix |
117
+ |---------|-----|
118
+ | "No Available Server" | Check `docker ps` for unhealthy containers |
119
+ | Variables not editable in UI | Use `${VAR}` syntax — not `VAR=value` |
120
+ | Magic variables not generating | Check spelling, `SERVICE_` prefix; requires Coolify ≥ v4.0.0-beta.411 |
121
+ | Port routing broken | Use `SERVICE_URL_NAME_PORT`, hyphen before port, remove `ports:` |
@@ -0,0 +1,77 @@
1
+ # Infrastructure & Diagnostics
2
+
3
+ ## Infrastructure Overview
4
+
5
+ Always start here — returns all servers, projects, apps, databases, services in one call:
6
+
7
+ ```
8
+ get_infrastructure_overview
9
+ ```
10
+
11
+ ## Server Management
12
+
13
+ ```bash
14
+ # List servers (summaries)
15
+ list_servers
16
+
17
+ # Full details
18
+ get_server uuid="<uuid>"
19
+
20
+ # Check resources on a server
21
+ server_resources uuid="<uuid>"
22
+
23
+ # List domains
24
+ server_domains uuid="<uuid>"
25
+
26
+ # Validate SSH connection
27
+ validate_server uuid="<uuid>"
28
+ ```
29
+
30
+ ## Diagnostics
31
+
32
+ Smart lookup — accepts names, domains, or IPs (not just UUIDs):
33
+
34
+ ```bash
35
+ # App diagnostics (by name or domain)
36
+ diagnose_app identifier="my-app"
37
+ diagnose_app identifier="example.com"
38
+
39
+ # Server diagnostics (by name or IP)
40
+ diagnose_server identifier="192.168.1.100"
41
+ diagnose_server identifier="coolify-apps"
42
+
43
+ # Scan all infrastructure
44
+ find_issues
45
+ ```
46
+
47
+ ## Batch Operations
48
+
49
+ ```bash
50
+ # Restart all apps in a project
51
+ restart_project_apps project_uuid="<uuid>"
52
+
53
+ # Redeploy all apps in a project
54
+ redeploy_project project_uuid="<uuid>"
55
+
56
+ # Emergency stop all apps (requires confirmation)
57
+ stop_all_apps confirm=true
58
+
59
+ # Bulk update env var across apps
60
+ bulk_env_update key="API_URL" value="https://new-api.example.com" uuids=["<uuid1>","<uuid2>"]
61
+ ```
62
+
63
+ ## Response Pattern
64
+
65
+ Coolify MCP returns HATEOAS-style `_actions` suggesting next steps:
66
+
67
+ ```json
68
+ {
69
+ "data": { "uuid": "abc123", "status": "running" },
70
+ "_actions": [
71
+ { "tool": "application_logs", "args": { "uuid": "abc123" }, "hint": "View logs" },
72
+ { "tool": "control", "args": { "resource": "application", "action": "restart", "uuid": "abc123" }, "hint": "Restart" }
73
+ ]
74
+ }
75
+ ```
76
+
77
+ Follow `_actions` suggestions for efficient multi-step workflows.
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: drizzle-orm
3
+ description: "Drizzle ORM schema definition, type-safe queries, relational queries, CRUD operations, transactions, migrations with drizzle-kit, and database setup for PostgreSQL, MySQL, and SQLite. Use when defining database schemas, writing queries or joins, managing migrations, setting up a new Drizzle project, or working with drizzle-kit."
4
+ ---
5
+
6
+ # Drizzle ORM
7
+
8
+ ## Topic Routing
9
+
10
+ Read the matching reference before writing code for any of these topics:
11
+
12
+ | Topic | Reference |
13
+ |-------|-----------|
14
+ | Schema definition & column types | `references/schema-patterns.md` |
15
+ | Queries, joins, & CRUD operations | `references/query-patterns.md` |
16
+ | Migrations & drizzle-kit | `references/migrations.md` |
17
+
18
+ ## Critical Rules
19
+
20
+ **Schema**
21
+ - Define schemas in dedicated `schema.ts` files using `pgTable` / `mysqlTable` / `sqliteTable` from the correct dialect package
22
+ - Use `$inferSelect` and `$inferInsert` for TypeScript types — never duplicate type definitions manually
23
+ - Foreign keys require explicit `references(() => table.column)` — omitting this creates an unconstrained column
24
+ - Pass `{ schema }` to `drizzle()` when initializing the client to enable relational queries
25
+
26
+ **Relations**
27
+ - Define with `relations()` from `drizzle-orm` alongside the table definition
28
+ - Relations are required for `db.query` relational API — the SQL-like API does not use them
29
+ - Do not use relations as a substitute for foreign key constraints; define both
30
+
31
+ **Queries**
32
+ - Use SQL-like API (`db.select().from()`) for complex joins and aggregations
33
+ - Use relational API (`db.query.table.findMany({ with: { ... } })`) for nested data fetching
34
+ - Import `eq`, `and`, `or`, `gt`, `like`, `isNull` etc. from `drizzle-orm` for `where` clauses
35
+ - Always use `returning()` to get the inserted, updated, or deleted rows back
36
+
37
+ **Migrations**
38
+ - Use `drizzle-kit` for all migrations: `npx drizzle-kit generate` then `npx drizzle-kit migrate`
39
+ - Configure in `drizzle.config.ts` — connections string must be set before running commands
40
+ - Never manually edit generated migration SQL files — regenerate if changes are needed
41
+ - Use `npx drizzle-kit push` in development only; always use `migrate` for production
42
+
43
+ **Transactions**
44
+ - Wrap multi-step operations in `db.transaction(async (tx) => { ... })`
45
+ - Use `tx` (the transaction argument) instead of `db` for all queries inside the callback
46
+
47
+ **Performance**
48
+ - Use `db.select({ col: table.col })` for partial selects — avoids loading unused columns
49
+ - Add indexes in the schema definition for frequently queried columns
50
+ - Use `.prepare()` for repeated queries (prepared statements)
51
+
52
+ ## Schema Definition
53
+
54
+ ```typescript
55
+ import { pgTable, text, integer, timestamp, boolean } from 'drizzle-orm/pg-core';
56
+ import { relations } from 'drizzle-orm';
57
+
58
+ export const users = pgTable('users', {
59
+ id: text('id').primaryKey(),
60
+ email: text('email').notNull().unique(),
61
+ name: text('name').notNull(),
62
+ createdAt: timestamp('created_at').defaultNow().notNull(),
63
+ });
64
+
65
+ export const posts = pgTable('posts', {
66
+ id: text('id').primaryKey(),
67
+ title: text('title').notNull(),
68
+ authorId: text('author_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
69
+ published: boolean('published').default(false).notNull(),
70
+ });
71
+
72
+ export const usersRelations = relations(users, ({ many }) => ({
73
+ posts: many(posts),
74
+ }));
75
+
76
+ export const postsRelations = relations(posts, ({ one }) => ({
77
+ author: one(users, { fields: [posts.authorId], references: [users.id] }),
78
+ }));
79
+
80
+ // Type inference — no manual duplication
81
+ export type User = typeof users.$inferSelect;
82
+ export type NewUser = typeof users.$inferInsert;
83
+ ```
84
+
85
+ ## Query Patterns
86
+
87
+ ```typescript
88
+ import { db } from './db';
89
+ import { eq } from 'drizzle-orm';
90
+ import { users, posts } from './schema';
91
+
92
+ // SQL-like: select with join
93
+ const results = await db
94
+ .select({ user: users, postCount: count(posts.id) })
95
+ .from(users)
96
+ .leftJoin(posts, eq(posts.authorId, users.id))
97
+ .groupBy(users.id);
98
+
99
+ // Relational: nested fetch
100
+ const usersWithPosts = await db.query.users.findMany({
101
+ where: eq(users.id, userId),
102
+ with: { posts: { where: eq(posts.published, true) } },
103
+ });
104
+
105
+ // Insert with returning
106
+ const [newUser] = await db.insert(users).values({ id, email, name }).returning();
107
+ ```
108
+
109
+ ## Reference Files
110
+
111
+ - `references/schema-patterns.md` — Table definitions, column types, constraints, indexes, relations, type inference
112
+ - `references/query-patterns.md` — Select, joins, where clauses, relational API, CRUD, transactions, prepared statements
113
+ - `references/migrations.md` — drizzle.config.ts, generate/migrate/push commands, migration workflow
114
+
115
+ ## Quick Workflow: Set up Drizzle in a project
116
+ 1. Install: `npm install drizzle-orm` + dialect driver (`postgres` / `@libsql/client` / `better-sqlite3`)
117
+ 2. Install drizzle-kit: `npm install -D drizzle-kit`
118
+ 3. Define schema in `src/db/schema.ts` using the correct dialect table builder
119
+ 4. Create `drizzle.config.ts` with database URL and schema path — verify the config before running commands
120
+ 5. Generate migration: `npx drizzle-kit generate` — inspect the SQL output before applying
121
+ 6. Apply migration: `npx drizzle-kit migrate`
122
+ - **If migration fails:** check DB connection string → verify schema matches existing tables → use `npx drizzle-kit push` for dev environments
123
+ 7. Initialize client: `const db = drizzle(pool, { schema })` and run a test query to confirm connectivity
@@ -0,0 +1,16 @@
1
+ import type { PluginConfig } from '../types.js';
2
+
3
+ export const config: PluginConfig = {
4
+ id: 'drizzle',
5
+ name: 'Drizzle ORM',
6
+ category: 'tech',
7
+ subCategory: 'database',
8
+ label: 'Drizzle ORM',
9
+ hint: 'Type-safe SQL ORM with migrations',
10
+ skillName: 'drizzle-orm',
11
+ authType: 'none',
12
+ envVars: [],
13
+ agentToolMap: {},
14
+ docsUrl: null,
15
+ officialDocs: 'https://orm.drizzle.team/',
16
+ };
@@ -0,0 +1,112 @@
1
+ # Drizzle Migrations
2
+
3
+ ## drizzle.config.ts
4
+
5
+ ```typescript
6
+ import { defineConfig } from 'drizzle-kit';
7
+
8
+ export default defineConfig({
9
+ dialect: 'postgresql', // 'postgresql' | 'mysql' | 'sqlite'
10
+ schema: './src/db/schema.ts', // path to your schema file(s)
11
+ out: './drizzle', // output directory for migrations
12
+ dbCredentials: {
13
+ url: process.env.DATABASE_URL!, // never hardcode credentials
14
+ },
15
+ verbose: true,
16
+ strict: true,
17
+ });
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ | Command | When to use |
23
+ |---------|------------|
24
+ | `npx drizzle-kit generate` | After schema changes — generates SQL migration files |
25
+ | `npx drizzle-kit migrate` | Apply pending migrations to the database (production-safe) |
26
+ | `npx drizzle-kit push` | Sync schema directly to DB without migrations (dev only) |
27
+ | `npx drizzle-kit studio` | Open local DB browser UI |
28
+ | `npx drizzle-kit check` | Validate migration history consistency |
29
+
30
+ ## Migration Workflow
31
+
32
+ ```bash
33
+ # 1. Change schema in src/db/schema.ts
34
+ # 2. Generate migration — inspect the SQL before applying
35
+ npx drizzle-kit generate
36
+
37
+ # 3. Review the generated .sql file in ./drizzle/
38
+ # 4. Apply to database
39
+ npx drizzle-kit migrate
40
+ ```
41
+
42
+ **Never edit generated migration files.** If the generated SQL is wrong, adjust the schema and regenerate.
43
+
44
+ ## Handling Breaking Changes
45
+
46
+ ### Adding a required (non-nullable) column
47
+
48
+ ```typescript
49
+ // Step 1: Add as nullable first
50
+ newColumn: text('new_column'),
51
+
52
+ // Step 2: Run migration to add the column → backfill data in a separate script/mutation
53
+ // Step 3: Add .notNull() after backfill is complete
54
+ newColumn: text('new_column').notNull(),
55
+
56
+ // Step 4: Run migration again to add the NOT NULL constraint
57
+ ```
58
+
59
+ ### Renaming a column
60
+
61
+ Drizzle Kit generates a DROP + ADD by default. To rename without data loss, use a custom migration:
62
+
63
+ ```sql
64
+ -- drizzle/0002_rename_column.sql (manually created)
65
+ ALTER TABLE users RENAME COLUMN old_name TO new_name;
66
+ ```
67
+
68
+ Then run `npx drizzle-kit migrate` — it will apply the custom file.
69
+
70
+ ## Programmatic Migrations (CI / startup)
71
+
72
+ ```typescript
73
+ import { drizzle } from 'drizzle-orm/postgres-js';
74
+ import { migrate } from 'drizzle-orm/postgres-js/migrator';
75
+ import postgres from 'postgres';
76
+
77
+ const migrationClient = postgres(process.env.DATABASE_URL!, { max: 1 });
78
+ await migrate(drizzle(migrationClient), { migrationsFolder: './drizzle' });
79
+ await migrationClient.end();
80
+ ```
81
+
82
+ ## Database Client Setup
83
+
84
+ ```typescript
85
+ // src/db/index.ts (PostgreSQL with postgres.js)
86
+ import { drizzle } from 'drizzle-orm/postgres-js';
87
+ import postgres from 'postgres';
88
+ import * as schema from './schema';
89
+
90
+ const pool = postgres(process.env.DATABASE_URL!);
91
+ export const db = drizzle(pool, { schema });
92
+ ```
93
+
94
+ ```typescript
95
+ // src/db/index.ts (SQLite with better-sqlite3)
96
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
97
+ import Database from 'better-sqlite3';
98
+ import * as schema from './schema';
99
+
100
+ const sqlite = new Database('sqlite.db');
101
+ export const db = drizzle(sqlite, { schema });
102
+ ```
103
+
104
+ ```typescript
105
+ // src/db/index.ts (Turso / libSQL)
106
+ import { drizzle } from 'drizzle-orm/libsql';
107
+ import { createClient } from '@libsql/client';
108
+ import * as schema from './schema';
109
+
110
+ const client = createClient({ url: process.env.TURSO_URL!, authToken: process.env.TURSO_TOKEN! });
111
+ export const db = drizzle(client, { schema });
112
+ ```
@@ -0,0 +1,127 @@
1
+ # Drizzle Query Patterns
2
+
3
+ ## SQL-like API (complex joins & aggregations)
4
+
5
+ ```typescript
6
+ import { db } from './db';
7
+ import { eq, and, or, gt, like, isNull, desc, count, sql } from 'drizzle-orm';
8
+ import { users, posts } from './schema';
9
+
10
+ // Basic select with where
11
+ const activeUsers = await db
12
+ .select()
13
+ .from(users)
14
+ .where(and(eq(users.active, true), gt(users.age, 18)));
15
+
16
+ // Partial select (avoids loading unused columns)
17
+ const emails = await db
18
+ .select({ id: users.id, email: users.email })
19
+ .from(users);
20
+
21
+ // Join
22
+ const postsWithAuthors = await db
23
+ .select({ post: posts, author: users })
24
+ .from(posts)
25
+ .innerJoin(users, eq(posts.authorId, users.id))
26
+ .where(eq(posts.published, true))
27
+ .orderBy(desc(posts.createdAt))
28
+ .limit(20);
29
+
30
+ // Aggregate
31
+ const stats = await db
32
+ .select({ total: count(), avgAge: sql<number>`avg(${users.age})` })
33
+ .from(users);
34
+ ```
35
+
36
+ ## Relational API (nested data fetching)
37
+
38
+ ```typescript
39
+ // Requires { schema } passed to drizzle() and relations defined in schema
40
+
41
+ // findMany with nested includes
42
+ const usersWithPosts = await db.query.users.findMany({
43
+ where: eq(users.active, true),
44
+ orderBy: desc(users.createdAt),
45
+ limit: 10,
46
+ with: {
47
+ posts: {
48
+ where: eq(posts.published, true),
49
+ columns: { id: true, title: true, createdAt: true }, // partial columns
50
+ orderBy: desc(posts.createdAt),
51
+ },
52
+ },
53
+ });
54
+
55
+ // findFirst
56
+ const user = await db.query.users.findFirst({
57
+ where: eq(users.email, email),
58
+ with: { posts: true },
59
+ });
60
+ ```
61
+
62
+ ## CRUD Operations
63
+
64
+ ```typescript
65
+ // Insert — always use returning() to get back the created row
66
+ const [newUser] = await db
67
+ .insert(users)
68
+ .values({ id: crypto.randomUUID(), email, name })
69
+ .returning();
70
+
71
+ // Upsert (on conflict)
72
+ const [upserted] = await db
73
+ .insert(users)
74
+ .values({ id, email, name })
75
+ .onConflictDoUpdate({
76
+ target: users.email,
77
+ set: { name, updatedAt: new Date() },
78
+ })
79
+ .returning();
80
+
81
+ // Update
82
+ const [updated] = await db
83
+ .update(users)
84
+ .set({ name: 'New Name', updatedAt: new Date() })
85
+ .where(eq(users.id, userId))
86
+ .returning();
87
+
88
+ // Delete
89
+ const [deleted] = await db
90
+ .delete(posts)
91
+ .where(and(eq(posts.authorId, userId), eq(posts.published, false)))
92
+ .returning();
93
+ ```
94
+
95
+ ## Transactions
96
+
97
+ ```typescript
98
+ const result = await db.transaction(async (tx) => {
99
+ // Use tx instead of db inside the transaction
100
+ const [order] = await tx.insert(orders).values(orderData).returning();
101
+
102
+ await tx.insert(orderItems).values(
103
+ items.map((item) => ({ orderId: order.id, ...item }))
104
+ );
105
+
106
+ await tx
107
+ .update(inventory)
108
+ .set({ quantity: sql`${inventory.quantity} - 1` })
109
+ .where(eq(inventory.productId, productId));
110
+
111
+ return order;
112
+ });
113
+ ```
114
+
115
+ ## Prepared Statements
116
+
117
+ ```typescript
118
+ // Define once, reuse many times
119
+ const getUserById = db
120
+ .select()
121
+ .from(users)
122
+ .where(eq(users.id, sql.placeholder('id')))
123
+ .prepare('get_user_by_id');
124
+
125
+ // Execute with parameters
126
+ const user = await getUserById.execute({ id: userId });
127
+ ```
@@ -0,0 +1,105 @@
1
+ # Drizzle Schema Patterns
2
+
3
+ ## PostgreSQL
4
+
5
+ ```typescript
6
+ import {
7
+ pgTable, text, integer, boolean, timestamp, numeric,
8
+ serial, uuid, jsonb, index, uniqueIndex,
9
+ } from 'drizzle-orm/pg-core';
10
+ import { relations, sql } from 'drizzle-orm';
11
+
12
+ export const users = pgTable('users', {
13
+ id: uuid('id').defaultRandom().primaryKey(),
14
+ email: text('email').notNull().unique(),
15
+ name: text('name').notNull(),
16
+ age: integer('age'),
17
+ active: boolean('active').default(true).notNull(),
18
+ metadata: jsonb('metadata').$type<Record<string, unknown>>(),
19
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
20
+ updatedAt: timestamp('updated_at', { withTimezone: true })
21
+ .defaultNow()
22
+ .$onUpdate(() => new Date())
23
+ .notNull(),
24
+ }, (table) => [
25
+ index('users_email_idx').on(table.email),
26
+ uniqueIndex('users_active_email_idx').on(table.active, table.email),
27
+ ]);
28
+ ```
29
+
30
+ ## MySQL
31
+
32
+ ```typescript
33
+ import { mysqlTable, varchar, int, boolean, datetime } from 'drizzle-orm/mysql-core';
34
+
35
+ export const products = mysqlTable('products', {
36
+ id: int('id').autoincrement().primaryKey(),
37
+ name: varchar('name', { length: 255 }).notNull(),
38
+ price: int('price').notNull(), // store in cents
39
+ inStock: boolean('in_stock').default(true),
40
+ createdAt: datetime('created_at').default(sql`now()`),
41
+ });
42
+ ```
43
+
44
+ ## SQLite
45
+
46
+ ```typescript
47
+ import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
48
+ import { sql } from 'drizzle-orm';
49
+
50
+ export const notes = sqliteTable('notes', {
51
+ id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }),
52
+ title: text('title').notNull(),
53
+ body: text('body'),
54
+ score: real('score').default(0),
55
+ createdAt: integer('created_at', { mode: 'timestamp' })
56
+ .default(sql`(unixepoch())`)
57
+ .notNull(),
58
+ });
59
+ ```
60
+
61
+ ## Foreign Keys & Constraints
62
+
63
+ ```typescript
64
+ export const posts = pgTable('posts', {
65
+ id: uuid('id').defaultRandom().primaryKey(),
66
+ authorId: uuid('author_id')
67
+ .notNull()
68
+ .references(() => users.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
69
+ categoryId: uuid('category_id')
70
+ .references(() => categories.id, { onDelete: 'set null' }), // nullable FK
71
+ });
72
+ ```
73
+
74
+ ## Relations
75
+
76
+ ```typescript
77
+ import { relations } from 'drizzle-orm';
78
+
79
+ // One-to-many
80
+ export const usersRelations = relations(users, ({ many }) => ({
81
+ posts: many(posts),
82
+ }));
83
+
84
+ export const postsRelations = relations(posts, ({ one, many }) => ({
85
+ author: one(users, { fields: [posts.authorId], references: [users.id] }),
86
+ comments: many(comments),
87
+ }));
88
+
89
+ // Many-to-many (via junction table)
90
+ export const postTagsRelations = relations(postTags, ({ one }) => ({
91
+ post: one(posts, { fields: [postTags.postId], references: [posts.id] }),
92
+ tag: one(tags, { fields: [postTags.tagId], references: [tags.id] }),
93
+ }));
94
+ ```
95
+
96
+ ## Type Inference
97
+
98
+ ```typescript
99
+ // Always use $inferSelect and $inferInsert — never write these types manually
100
+ export type User = typeof users.$inferSelect;
101
+ export type NewUser = typeof users.$inferInsert;
102
+
103
+ // Partial insert type (e.g. when ID is generated externally)
104
+ export type CreateUserInput = Omit<NewUser, 'id' | 'createdAt' | 'updatedAt'>;
105
+ ```