@jgamaraalv/ts-dev-kit 1.0.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-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/agents/accessibility-pro.md +139 -0
- package/agents/api-builder.md +110 -0
- package/agents/code-reviewer.md +190 -0
- package/agents/database-expert.md +138 -0
- package/agents/debugger.md +241 -0
- package/agents/docker-expert.md +51 -0
- package/agents/multi-agent-coordinator.md +378 -0
- package/agents/nextjs-expert.md +136 -0
- package/agents/performance-engineer.md +138 -0
- package/agents/playwright-expert.md +126 -0
- package/agents/react-specialist.md +97 -0
- package/agents/security-scanner.md +105 -0
- package/agents/test-generator.md +221 -0
- package/agents/typescript-pro.md +253 -0
- package/agents/ux-optimizer.md +93 -0
- package/docs/rules/orchestration.md.template +126 -0
- package/package.json +28 -0
- package/skills/bullmq/SKILL.md +225 -0
- package/skills/bullmq/references/flows-and-schedulers.md +186 -0
- package/skills/bullmq/references/job-types-and-options.md +163 -0
- package/skills/bullmq/references/patterns.md +273 -0
- package/skills/bullmq/references/production.md +308 -0
- package/skills/composition-patterns/SKILL.md +58 -0
- package/skills/composition-patterns/references/architecture-avoid-boolean-props.md +87 -0
- package/skills/composition-patterns/references/architecture-compound-components.md +107 -0
- package/skills/composition-patterns/references/patterns-children-over-render-props.md +77 -0
- package/skills/composition-patterns/references/patterns-explicit-variants.md +87 -0
- package/skills/composition-patterns/references/react19-no-forwardref.md +37 -0
- package/skills/composition-patterns/references/state-context-interface.md +194 -0
- package/skills/composition-patterns/references/state-decouple-implementation.md +96 -0
- package/skills/composition-patterns/references/state-lift-state.md +126 -0
- package/skills/conventional-commits/SKILL.md +148 -0
- package/skills/docker/SKILL.md +55 -0
- package/skills/docker/references/compose-configs.md +95 -0
- package/skills/docker/references/monorepo-dockerfile.md +111 -0
- package/skills/drizzle-pg/SKILL.md +202 -0
- package/skills/drizzle-pg/references/advanced.md +299 -0
- package/skills/drizzle-pg/references/migrations.md +214 -0
- package/skills/drizzle-pg/references/queries.md +321 -0
- package/skills/drizzle-pg/references/relations.md +272 -0
- package/skills/drizzle-pg/references/schema-pg.md +256 -0
- package/skills/drizzle-pg/references/sql-operator.md +215 -0
- package/skills/fastify-best-practices/SKILL.md +143 -0
- package/skills/fastify-best-practices/references/hooks-and-lifecycle.md +122 -0
- package/skills/fastify-best-practices/references/plugins-and-encapsulation.md +137 -0
- package/skills/fastify-best-practices/references/request-reply-errors.md +189 -0
- package/skills/fastify-best-practices/references/routes-and-handlers.md +134 -0
- package/skills/fastify-best-practices/references/server-and-options.md +127 -0
- package/skills/fastify-best-practices/references/typescript-and-logging.md +223 -0
- package/skills/fastify-best-practices/references/validation-and-serialization.md +190 -0
- package/skills/ioredis/SKILL.md +51 -0
- package/skills/ioredis/references/advanced-patterns.md +312 -0
- package/skills/ioredis/references/cluster-sentinel.md +280 -0
- package/skills/ioredis/references/connection-options.md +187 -0
- package/skills/ioredis/references/core-api.md +179 -0
- package/skills/nextjs-best-practices/SKILL.md +194 -0
- package/skills/nextjs-best-practices/references/async-patterns.md +84 -0
- package/skills/nextjs-best-practices/references/bundling.md +192 -0
- package/skills/nextjs-best-practices/references/data-patterns.md +310 -0
- package/skills/nextjs-best-practices/references/debug-tricks.md +127 -0
- package/skills/nextjs-best-practices/references/directives.md +74 -0
- package/skills/nextjs-best-practices/references/error-handling.md +237 -0
- package/skills/nextjs-best-practices/references/file-conventions.md +152 -0
- package/skills/nextjs-best-practices/references/font.md +175 -0
- package/skills/nextjs-best-practices/references/functions.md +116 -0
- package/skills/nextjs-best-practices/references/hydration-error.md +86 -0
- package/skills/nextjs-best-practices/references/image.md +184 -0
- package/skills/nextjs-best-practices/references/metadata.md +305 -0
- package/skills/nextjs-best-practices/references/parallel-routes.md +299 -0
- package/skills/nextjs-best-practices/references/route-handlers.md +154 -0
- package/skills/nextjs-best-practices/references/rsc-boundaries.md +168 -0
- package/skills/nextjs-best-practices/references/runtime-selection.md +40 -0
- package/skills/nextjs-best-practices/references/scripts.md +148 -0
- package/skills/nextjs-best-practices/references/self-hosting.md +210 -0
- package/skills/nextjs-best-practices/references/suspense-boundaries.md +67 -0
- package/skills/owasp-security-review/SKILL.md +98 -0
- package/skills/owasp-security-review/references/a01-broken-access-control.md +78 -0
- package/skills/owasp-security-review/references/a02-security-misconfiguration.md +81 -0
- package/skills/owasp-security-review/references/a03-supply-chain-failures.md +65 -0
- package/skills/owasp-security-review/references/a04-cryptographic-failures.md +82 -0
- package/skills/owasp-security-review/references/a05-injection.md +106 -0
- package/skills/owasp-security-review/references/a06-insecure-design.md +76 -0
- package/skills/owasp-security-review/references/a07-authentication-failures.md +83 -0
- package/skills/owasp-security-review/references/a08-integrity-failures.md +72 -0
- package/skills/owasp-security-review/references/a09-logging-alerting-failures.md +76 -0
- package/skills/owasp-security-review/references/a10-exceptional-conditions.md +131 -0
- package/skills/postgresql/SKILL.md +50 -0
- package/skills/postgresql/references/ddl-schema.md +300 -0
- package/skills/postgresql/references/indexes.md +257 -0
- package/skills/postgresql/references/jsonb.md +261 -0
- package/skills/postgresql/references/performance.md +291 -0
- package/skills/postgresql/references/psql-cli.md +153 -0
- package/skills/postgresql/references/queries.md +287 -0
- package/skills/postgresql/references/transactions.md +280 -0
- package/skills/react-best-practices/SKILL.md +110 -0
- package/skills/react-best-practices/references/advanced-patterns.md +91 -0
- package/skills/react-best-practices/references/async-patterns.md +233 -0
- package/skills/react-best-practices/references/bundle-optimization.md +201 -0
- package/skills/react-best-practices/references/client-patterns.md +178 -0
- package/skills/react-best-practices/references/js-performance.md +210 -0
- package/skills/react-best-practices/references/rendering-performance.md +209 -0
- package/skills/react-best-practices/references/rerender-optimization.md +316 -0
- package/skills/react-best-practices/references/server-performance.md +274 -0
- package/skills/service-worker/SKILL.md +195 -0
- package/skills/service-worker/references/api-reference.md +114 -0
- package/skills/service-worker/references/caching-strategies.md +202 -0
- package/skills/service-worker/references/push-and-sync.md +261 -0
- package/skills/typescript-conventions/SKILL.md +51 -0
- package/skills/ui-ux-guidelines/SKILL.md +105 -0
- package/skills/ui-ux-guidelines/references/accessibility-and-interaction.md +74 -0
- package/skills/ui-ux-guidelines/references/forms-content-checklist.md +126 -0
- package/skills/ui-ux-guidelines/references/layout-typography-animation.md +95 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: drizzle-pg
|
|
3
|
+
description: "Drizzle ORM reference for PostgreSQL — schema definition, typesafe queries, relations, and migrations with drizzle-kit. Use when: (1) defining pgTable schemas with column types, indexes, constraints, or enums, (2) writing select/insert/update/delete queries or joins, (3) defining relations and using the relational query API (db.query.*), (4) running drizzle-kit generate/migrate/push/pull, (5) configuring drizzle.config.ts, (6) using the sql`` template operator, or (7) working with PostGIS/pg_vector extensions."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Drizzle ORM — PostgreSQL
|
|
7
|
+
|
|
8
|
+
Drizzle is a headless TypeScript ORM. Zero dependencies, SQL-like API, single-query output.
|
|
9
|
+
Packages: `drizzle-orm` (runtime), `drizzle-kit` (CLI/migrations).
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Quick Start](#quick-start)
|
|
14
|
+
- [Import Cheat Sheet](#import-cheat-sheet)
|
|
15
|
+
- [Common Patterns](#common-patterns)
|
|
16
|
+
- [Reference Files](#reference-files)
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Connect
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
24
|
+
import * as schema from "./schema";
|
|
25
|
+
import { relations } from "./relations";
|
|
26
|
+
|
|
27
|
+
const db = drizzle(process.env.DATABASE_URL, { schema, relations });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or with existing Pool:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Pool } from "pg";
|
|
34
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
35
|
+
const db = drizzle({ client: pool, schema, relations });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Define Schema
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import {
|
|
42
|
+
pgTable,
|
|
43
|
+
pgEnum,
|
|
44
|
+
serial,
|
|
45
|
+
text,
|
|
46
|
+
integer,
|
|
47
|
+
timestamp,
|
|
48
|
+
uuid,
|
|
49
|
+
jsonb,
|
|
50
|
+
index,
|
|
51
|
+
uniqueIndex,
|
|
52
|
+
} from "drizzle-orm/pg-core";
|
|
53
|
+
import { sql } from "drizzle-orm";
|
|
54
|
+
|
|
55
|
+
export const statusEnum = pgEnum("status", ["active", "inactive", "banned"]);
|
|
56
|
+
|
|
57
|
+
export const users = pgTable(
|
|
58
|
+
"users",
|
|
59
|
+
{
|
|
60
|
+
id: uuid("id")
|
|
61
|
+
.default(sql`gen_random_uuid()`)
|
|
62
|
+
.primaryKey(),
|
|
63
|
+
name: text("name").notNull(),
|
|
64
|
+
email: text("email").notNull().unique(),
|
|
65
|
+
status: statusEnum().default("active").notNull(),
|
|
66
|
+
metadata: jsonb("metadata").$type<{ roles: string[] }>(),
|
|
67
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
68
|
+
},
|
|
69
|
+
(t) => [index("users_email_idx").on(t.email)],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export const posts = pgTable("posts", {
|
|
73
|
+
id: serial("id").primaryKey(),
|
|
74
|
+
title: text("title").notNull(),
|
|
75
|
+
authorId: uuid("author_id")
|
|
76
|
+
.notNull()
|
|
77
|
+
.references(() => users.id, { onDelete: "cascade" }),
|
|
78
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Define Relations
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { defineRelations } from "drizzle-orm";
|
|
86
|
+
import * as schema from "./schema";
|
|
87
|
+
|
|
88
|
+
export const relations = defineRelations(schema, (r) => ({
|
|
89
|
+
users: {
|
|
90
|
+
posts: r.many.posts({ from: r.users.id, to: r.posts.authorId }),
|
|
91
|
+
},
|
|
92
|
+
posts: {
|
|
93
|
+
author: r.one.users({ from: r.posts.authorId, to: r.users.id }),
|
|
94
|
+
},
|
|
95
|
+
}));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### CRUD
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { eq, and, ilike, sql } from "drizzle-orm";
|
|
102
|
+
|
|
103
|
+
// SELECT
|
|
104
|
+
const allUsers = await db.select().from(users);
|
|
105
|
+
const user = await db.select().from(users).where(eq(users.id, id));
|
|
106
|
+
|
|
107
|
+
// INSERT
|
|
108
|
+
const [created] = await db
|
|
109
|
+
.insert(users)
|
|
110
|
+
.values({ name: "Dan", email: "dan@example.com" })
|
|
111
|
+
.returning();
|
|
112
|
+
|
|
113
|
+
// UPDATE
|
|
114
|
+
await db.update(users).set({ name: "Updated" }).where(eq(users.id, id));
|
|
115
|
+
|
|
116
|
+
// DELETE
|
|
117
|
+
await db.delete(users).where(eq(users.id, id));
|
|
118
|
+
|
|
119
|
+
// UPSERT
|
|
120
|
+
await db
|
|
121
|
+
.insert(users)
|
|
122
|
+
.values({ id, name: "Dan", email: "dan@ex.com" })
|
|
123
|
+
.onConflictDoUpdate({ target: users.id, set: { name: "Dan" } });
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Relational Queries
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// Nested eager loading (single SQL query)
|
|
130
|
+
const usersWithPosts = await db.query.users.findMany({
|
|
131
|
+
with: { posts: true },
|
|
132
|
+
where: { status: "active" },
|
|
133
|
+
orderBy: { createdAt: "desc" },
|
|
134
|
+
limit: 10,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const user = await db.query.users.findFirst({
|
|
138
|
+
where: { id: userId },
|
|
139
|
+
with: { posts: { columns: { id: true, title: true } } },
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Migrations
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# drizzle.config.ts -> see references/migrations.md
|
|
147
|
+
npx drizzle-kit generate # schema diff -> SQL files
|
|
148
|
+
npx drizzle-kit migrate # apply SQL to database
|
|
149
|
+
npx drizzle-kit push # direct push (no SQL files)
|
|
150
|
+
npx drizzle-kit pull # introspect DB -> Drizzle schema
|
|
151
|
+
npx drizzle-kit studio # visual browser UI
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Import Cheat Sheet
|
|
155
|
+
|
|
156
|
+
| Import path | Key exports |
|
|
157
|
+
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `drizzle-orm/pg-core` | `pgTable`, `pgEnum`, column types (`serial`, `text`, `integer`, `uuid`, `timestamp`, `jsonb`, `varchar`, `boolean`, `numeric`, `bigint`, `geometry`, `vector`, ...), `index`, `uniqueIndex`, `unique`, `check`, `primaryKey`, `foreignKey` |
|
|
159
|
+
| `drizzle-orm` | Operators: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `and`, `or`, `not`, `isNull`, `isNotNull`, `inArray`, `between`, `like`, `ilike`, `exists`, `sql`, `asc`, `desc`. Utilities: `getColumns`, `defineRelations`, `cosineDistance`, `l2Distance` |
|
|
160
|
+
| `drizzle-orm` (types) | `InferSelectModel`, `InferInsertModel` |
|
|
161
|
+
| `drizzle-zod` | `createInsertSchema`, `createSelectSchema` |
|
|
162
|
+
|
|
163
|
+
## Common Patterns
|
|
164
|
+
|
|
165
|
+
### Conditional filters
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const filters: SQL[] = [];
|
|
169
|
+
if (name) filters.push(ilike(users.name, `%${name}%`));
|
|
170
|
+
if (status) filters.push(eq(users.status, status));
|
|
171
|
+
await db
|
|
172
|
+
.select()
|
|
173
|
+
.from(users)
|
|
174
|
+
.where(and(...filters));
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Transactions
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
await db.transaction(async (tx) => {
|
|
181
|
+
const [user] = await tx.insert(users).values({ name: "Dan" }).returning();
|
|
182
|
+
await tx.insert(posts).values({ title: "Hello", authorId: user.id });
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Type inference
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
type User = typeof users.$inferSelect;
|
|
190
|
+
type NewUser = typeof users.$inferInsert;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Reference Files
|
|
194
|
+
|
|
195
|
+
For detailed API coverage, see:
|
|
196
|
+
|
|
197
|
+
- **Column types, indexes, constraints, enums, PostGIS, pg_vector**: [references/schema-pg.md](references/schema-pg.md)
|
|
198
|
+
- **Select, insert, update, delete, joins, filters**: [references/queries.md](references/queries.md)
|
|
199
|
+
- **Relations definition, relational query API (findMany/findFirst)**: [references/relations.md](references/relations.md)
|
|
200
|
+
- **sql`` template: raw, empty, join, identifier, placeholders**: [references/sql-operator.md](references/sql-operator.md)
|
|
201
|
+
- **drizzle-kit commands, drizzle.config.ts, migration workflows**: [references/migrations.md](references/migrations.md)
|
|
202
|
+
- **Dynamic queries, transactions, custom types, Zod, utilities**: [references/advanced.md](references/advanced.md)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Advanced Features Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Type Inference](#type-inference)
|
|
6
|
+
- [Dynamic Query Building](#dynamic-query-building)
|
|
7
|
+
- [Transactions](#transactions)
|
|
8
|
+
- [Utility Functions](#utility-functions)
|
|
9
|
+
- [Logging](#logging)
|
|
10
|
+
- [Custom Types](#custom-types)
|
|
11
|
+
- [Table Name Prefixing](#table-name-prefixing)
|
|
12
|
+
- [Standalone Query Builder](#standalone-query-builder)
|
|
13
|
+
- [Zod Integration](#zod-integration)
|
|
14
|
+
- [Anti-Patterns](#anti-patterns)
|
|
15
|
+
|
|
16
|
+
## Type Inference
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Infer select/insert types from table definition
|
|
20
|
+
type SelectUser = typeof users.$inferSelect;
|
|
21
|
+
type InsertUser = typeof users.$inferInsert;
|
|
22
|
+
|
|
23
|
+
// Alternative with utility types
|
|
24
|
+
import type { InferSelectModel, InferInsertModel } from "drizzle-orm";
|
|
25
|
+
type SelectUser = InferSelectModel<typeof users>;
|
|
26
|
+
type InsertUser = InferInsertModel<typeof users>;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Dynamic Query Building
|
|
30
|
+
|
|
31
|
+
Use `$dynamic()` to remove the restriction that query methods can only be called once:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import type { PgSelect } from "drizzle-orm/pg-core";
|
|
35
|
+
|
|
36
|
+
function withPagination<T extends PgSelect>(qb: T, page = 1, pageSize = 10) {
|
|
37
|
+
return qb.limit(pageSize).offset((page - 1) * pageSize);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function withOrderBy<T extends PgSelect>(qb: T, column: PgColumn, dir: "asc" | "desc" = "asc") {
|
|
41
|
+
return qb.orderBy(dir === "asc" ? asc(column) : desc(column));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Usage
|
|
45
|
+
const query = db.select().from(users).where(eq(users.active, true)).$dynamic();
|
|
46
|
+
const result = await withPagination(withOrderBy(query, users.createdAt, "desc"), 2, 20);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Available types by operation: `PgSelect`, `PgInsert`, `PgUpdate`, `PgDelete`, `PgSelectQueryBuilder`.
|
|
50
|
+
|
|
51
|
+
## Transactions
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
await db.transaction(async (tx) => {
|
|
55
|
+
const [user] = await tx.insert(users).values({ name: "Dan" }).returning();
|
|
56
|
+
await tx.insert(posts).values({ title: "Hello", authorId: user.id });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Nested savepoints
|
|
60
|
+
await db.transaction(async (tx) => {
|
|
61
|
+
await tx.insert(users).values({ name: "Dan" });
|
|
62
|
+
|
|
63
|
+
await tx.transaction(async (tx2) => {
|
|
64
|
+
// This is a savepoint
|
|
65
|
+
await tx2.insert(posts).values({ title: "Nested" });
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Rollback
|
|
70
|
+
await db.transaction(async (tx) => {
|
|
71
|
+
await tx.insert(users).values({ name: "Dan" });
|
|
72
|
+
tx.rollback(); // Rolls back entire transaction
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Isolation level
|
|
76
|
+
await db.transaction(
|
|
77
|
+
async (tx) => {
|
|
78
|
+
// ...
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
isolationLevel: "serializable", // "read uncommitted" | "read committed" | "repeatable read" | "serializable"
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Utility Functions
|
|
87
|
+
|
|
88
|
+
### getColumns — extract columns (exclude sensitive fields)
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { getColumns } from "drizzle-orm";
|
|
92
|
+
|
|
93
|
+
const { password, ...publicColumns } = getColumns(users);
|
|
94
|
+
await db.select(publicColumns).from(users);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### getTableConfig — table metadata
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { getTableConfig } from "drizzle-orm/pg-core";
|
|
101
|
+
|
|
102
|
+
const config = getTableConfig(users);
|
|
103
|
+
// config.columns, config.indexes, config.foreignKeys, config.checks, config.primaryKeys, config.name, config.schema
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### toSQL — inspect generated SQL
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const query = db.select().from(users).where(eq(users.id, 1)).toSQL();
|
|
110
|
+
// { sql: "select ... from \"users\" where \"users\".\"id\" = $1", params: [1] }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### db.execute — raw SQL
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const result = await db.execute(sql`SELECT * FROM users WHERE id = ${userId}`);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### is — runtime type checking
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { is, Column } from "drizzle-orm";
|
|
123
|
+
|
|
124
|
+
if (is(value, Column)) {
|
|
125
|
+
// value narrowed to Column type
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Logging
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Built-in logger
|
|
133
|
+
const db = drizzle(process.env.DATABASE_URL, { logger: true });
|
|
134
|
+
|
|
135
|
+
// Custom logger
|
|
136
|
+
import type { Logger } from "drizzle-orm";
|
|
137
|
+
|
|
138
|
+
class MyLogger implements Logger {
|
|
139
|
+
logQuery(query: string, params: unknown[]): void {
|
|
140
|
+
console.log({ query, params });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const db = drizzle(process.env.DATABASE_URL, { logger: new MyLogger() });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Custom Types
|
|
148
|
+
|
|
149
|
+
Define custom column types for special mappings:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
153
|
+
|
|
154
|
+
// Example: monetary type stored as integer cents
|
|
155
|
+
const money = customType<{ data: number; driverData: number }>({
|
|
156
|
+
dataType() {
|
|
157
|
+
return "integer";
|
|
158
|
+
},
|
|
159
|
+
fromDriver(value: number): number {
|
|
160
|
+
return value / 100;
|
|
161
|
+
},
|
|
162
|
+
toDriver(value: number): number {
|
|
163
|
+
return Math.round(value * 100);
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Usage
|
|
168
|
+
const products = pgTable("products", {
|
|
169
|
+
price: money("price_cents").notNull(),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Example: JSONB with typed shape
|
|
173
|
+
const typedJsonb = <T>(name: string) =>
|
|
174
|
+
customType<{ data: T; driverData: string }>({
|
|
175
|
+
dataType() {
|
|
176
|
+
return "jsonb";
|
|
177
|
+
},
|
|
178
|
+
toDriver(value: T): string {
|
|
179
|
+
return JSON.stringify(value);
|
|
180
|
+
},
|
|
181
|
+
})(name);
|
|
182
|
+
|
|
183
|
+
const settings = pgTable("settings", {
|
|
184
|
+
config: typedJsonb<{ theme: string; lang: string }>("config"),
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### customType parameters
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
customType<{
|
|
192
|
+
data: AppType; // Required — the TS type for select/insert
|
|
193
|
+
driverData: DriverType; // Type the driver returns
|
|
194
|
+
config: ConfigType; // Config object shape for dataType()
|
|
195
|
+
}>({
|
|
196
|
+
dataType(config): string; // Returns SQL type string
|
|
197
|
+
toDriver?(value): driverData; // App → Database
|
|
198
|
+
fromDriver?(value): data; // Database → App
|
|
199
|
+
})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Table Name Prefixing
|
|
203
|
+
|
|
204
|
+
Useful for multi-project schemas sharing one database:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { pgTableCreator } from "drizzle-orm/pg-core";
|
|
208
|
+
|
|
209
|
+
const pgTable = pgTableCreator((name) => `myapp_${name}`);
|
|
210
|
+
|
|
211
|
+
const users = pgTable("users", {
|
|
212
|
+
id: serial("id").primaryKey(),
|
|
213
|
+
});
|
|
214
|
+
// Creates table "myapp_users" in database
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Standalone Query Builder
|
|
218
|
+
|
|
219
|
+
Build queries without a database connection (for inspection or testing):
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { QueryBuilder } from "drizzle-orm/pg-core";
|
|
223
|
+
|
|
224
|
+
const qb = new QueryBuilder();
|
|
225
|
+
const query = qb.select().from(users).where(eq(users.name, "Dan"));
|
|
226
|
+
const { sql, params } = query.toSQL();
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Zod Integration
|
|
230
|
+
|
|
231
|
+
Generate Zod schemas from Drizzle tables:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
|
235
|
+
|
|
236
|
+
const insertUserSchema = createInsertSchema(users);
|
|
237
|
+
const selectUserSchema = createSelectSchema(users);
|
|
238
|
+
|
|
239
|
+
// With refinements
|
|
240
|
+
const insertUserSchema = createInsertSchema(users, {
|
|
241
|
+
email: (schema) => schema.email(),
|
|
242
|
+
name: (schema) => schema.min(1).max(100),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Usage with Fastify/Express
|
|
246
|
+
const parsed = insertUserSchema.parse(req.body);
|
|
247
|
+
await db.insert(users).values(parsed);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Requires `drizzle-zod` package: `npm i drizzle-zod`
|
|
251
|
+
|
|
252
|
+
## Anti-Patterns
|
|
253
|
+
|
|
254
|
+
### Raw SQL with user input (SQL injection risk)
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// BAD -- string interpolation bypasses parameterization
|
|
258
|
+
const name = req.query.name;
|
|
259
|
+
await db.execute(sql.raw(`SELECT * FROM users WHERE name = '${name}'`));
|
|
260
|
+
|
|
261
|
+
// GOOD -- use sql template literal for automatic parameterization
|
|
262
|
+
await db.execute(sql`SELECT * FROM users WHERE name = ${name}`);
|
|
263
|
+
|
|
264
|
+
// GOOD -- use Drizzle operators
|
|
265
|
+
await db.select().from(users).where(eq(users.name, name));
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`sql.raw()` should only be used for trusted, static SQL fragments (column names, table names), never for user-provided values.
|
|
269
|
+
|
|
270
|
+
### Missing `.returning()` on insert/update/delete
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// BAD -- no way to get the created record without a second query
|
|
274
|
+
await db.insert(users).values({ name: "Dan", email: "dan@ex.com" });
|
|
275
|
+
const user = await db.select().from(users).where(eq(users.email, "dan@ex.com"));
|
|
276
|
+
|
|
277
|
+
// GOOD -- get the result in a single round-trip
|
|
278
|
+
const [user] = await db.insert(users).values({ name: "Dan", email: "dan@ex.com" }).returning();
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Always use `.returning()` when you need the inserted/updated/deleted row. PostgreSQL supports this natively with no performance penalty.
|
|
282
|
+
|
|
283
|
+
### Mixing relational query API with SQL-like joins
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// BAD -- cannot use db.query.* (relational API) and then chain .leftJoin()
|
|
287
|
+
// The relational API (findMany/findFirst) and SQL-like API (select/from/join) are separate systems
|
|
288
|
+
const result = await db.query.users.findMany({
|
|
289
|
+
with: { posts: true },
|
|
290
|
+
}); // This returns nested objects
|
|
291
|
+
|
|
292
|
+
const result2 = await db.select().from(users).leftJoin(posts, eq(users.id, posts.authorId)); // This returns flat rows
|
|
293
|
+
|
|
294
|
+
// Pick one approach per query:
|
|
295
|
+
// - Relational API (db.query.*) for nested/eager loading with `with`
|
|
296
|
+
// - SQL-like API (db.select().from()) for joins, aggregations, complex SQL
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Do not attempt to mix `db.query.*` with `.leftJoin()`, `.innerJoin()`, or other SQL-like methods. They are different query builders with different return shapes.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Drizzle Kit & Migrations Reference
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Commands Overview](#commands-overview)
|
|
6
|
+
- [drizzle.config.ts](#drizzleconfigts)
|
|
7
|
+
- [Workflow: Code-First](#workflow-code-first)
|
|
8
|
+
- [Workflow: Direct Push](#workflow-direct-push)
|
|
9
|
+
- [Programmatic Migrations](#programmatic-migrations)
|
|
10
|
+
- [Custom Migrations](#custom-migrations)
|
|
11
|
+
- [CLI Flags](#cli-flags)
|
|
12
|
+
|
|
13
|
+
## Commands Overview
|
|
14
|
+
|
|
15
|
+
| Command | Purpose |
|
|
16
|
+
| -------------------------- | --------------------------------------------- |
|
|
17
|
+
| `npx drizzle-kit generate` | Generate SQL migration files from schema diff |
|
|
18
|
+
| `npx drizzle-kit migrate` | Apply pending SQL migrations to database |
|
|
19
|
+
| `npx drizzle-kit push` | Push schema directly to DB (no SQL files) |
|
|
20
|
+
| `npx drizzle-kit pull` | Introspect DB and generate Drizzle schema |
|
|
21
|
+
| `npx drizzle-kit check` | Check migrations for race conditions |
|
|
22
|
+
| `npx drizzle-kit up` | Upgrade migration snapshots to latest format |
|
|
23
|
+
| `npx drizzle-kit studio` | Open Drizzle Studio UI (browser) |
|
|
24
|
+
|
|
25
|
+
## drizzle.config.ts
|
|
26
|
+
|
|
27
|
+
### Minimal (PostgreSQL)
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { defineConfig } from "drizzle-kit";
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
dialect: "postgresql",
|
|
34
|
+
schema: "./src/db/schema.ts",
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Full options
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { defineConfig } from "drizzle-kit";
|
|
42
|
+
|
|
43
|
+
export default defineConfig({
|
|
44
|
+
// Required
|
|
45
|
+
dialect: "postgresql", // "postgresql" | "mysql" | "sqlite" | "turso" | "singlestore" | "mssql" | "cockroachdb"
|
|
46
|
+
schema: "./src/db/schema.ts", // string | string[] — glob patterns supported
|
|
47
|
+
|
|
48
|
+
// Migration output
|
|
49
|
+
out: "./drizzle", // default: "./drizzle"
|
|
50
|
+
breakpoints: true, // add statement breakpoints in SQL
|
|
51
|
+
|
|
52
|
+
// Database connection (for migrate/push/pull/studio)
|
|
53
|
+
dbCredentials: {
|
|
54
|
+
url: process.env.DATABASE_URL!,
|
|
55
|
+
// or individual fields:
|
|
56
|
+
// host: "localhost", port: 5432, user: "postgres", password: "pass", database: "mydb", ssl: true
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// Migration tracking table
|
|
60
|
+
migrations: {
|
|
61
|
+
table: "__drizzle_migrations", // default
|
|
62
|
+
schema: "drizzle", // default (PG schema)
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Filtering
|
|
66
|
+
tablesFilter: ["*"], // include specific tables: ["users", "posts"]
|
|
67
|
+
schemaFilter: ["public"], // PG schemas to include
|
|
68
|
+
extensionsFilters: ["postgis"], // skip extension-managed tables
|
|
69
|
+
|
|
70
|
+
// Introspection (pull)
|
|
71
|
+
introspect: {
|
|
72
|
+
casing: "camel", // "camel" (default) | "preserve"
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Push behavior
|
|
76
|
+
strict: false, // require approval before executing
|
|
77
|
+
verbose: true, // print SQL before execution
|
|
78
|
+
|
|
79
|
+
// Roles (PG-specific)
|
|
80
|
+
entities: {
|
|
81
|
+
roles: false, // true | false | { provider: "neon" | "supabase", include/exclude: [...] }
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Environment variables pattern
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { defineConfig } from "drizzle-kit";
|
|
90
|
+
|
|
91
|
+
export default defineConfig({
|
|
92
|
+
dialect: "postgresql",
|
|
93
|
+
schema: "./src/db/schema.ts",
|
|
94
|
+
out: "./drizzle",
|
|
95
|
+
dbCredentials: {
|
|
96
|
+
url: process.env.DATABASE_URL!,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Multiple schema files
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
schema: "./src/db/schema/*.ts"; // glob
|
|
105
|
+
schema: ["./src/db/users.ts", "./src/db/posts.ts"]; // explicit array
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Workflow: Code-First
|
|
109
|
+
|
|
110
|
+
Best for production, team collaboration, and audit trails.
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
1. Edit TypeScript schema
|
|
114
|
+
2. npx drizzle-kit generate → creates timestamped SQL in ./drizzle/
|
|
115
|
+
3. Review generated .sql files
|
|
116
|
+
4. npx drizzle-kit migrate → applies to database
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Output structure:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
drizzle/
|
|
123
|
+
├── 0000_init.sql
|
|
124
|
+
├── 0001_add_posts.sql
|
|
125
|
+
├── meta/
|
|
126
|
+
│ ├── 0000_snapshot.json
|
|
127
|
+
│ ├── 0001_snapshot.json
|
|
128
|
+
│ └── _journal.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Applied migrations tracked in `__drizzle_migrations` table.
|
|
132
|
+
|
|
133
|
+
### Programmatic migrate
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
137
|
+
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
138
|
+
|
|
139
|
+
const db = drizzle(process.env.DATABASE_URL);
|
|
140
|
+
|
|
141
|
+
await migrate(db, { migrationsFolder: "./drizzle" });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Workflow: Direct Push
|
|
145
|
+
|
|
146
|
+
Best for rapid prototyping, local dev, serverless databases.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
1. Edit TypeScript schema
|
|
150
|
+
2. npx drizzle-kit push → diffs and applies directly
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
No SQL files generated. Use `--strict` for approval prompts, `--force` to auto-accept data-loss.
|
|
154
|
+
|
|
155
|
+
## Custom Migrations
|
|
156
|
+
|
|
157
|
+
Generate empty migration for manual SQL (seeding, unsupported DDL):
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx drizzle-kit generate --custom --name=seed-users
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Creates empty `.sql` file to fill in:
|
|
164
|
+
|
|
165
|
+
```sql
|
|
166
|
+
-- Custom migration: seed-users
|
|
167
|
+
INSERT INTO "users" ("name", "email") VALUES ('Dan', 'dan@example.com');
|
|
168
|
+
INSERT INTO "users" ("name", "email") VALUES ('Andrew', 'andrew@example.com');
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## CLI Flags
|
|
172
|
+
|
|
173
|
+
### generate
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
--name=<name> Custom migration name
|
|
177
|
+
--custom Generate empty SQL for manual migration
|
|
178
|
+
--config=<path> Config file path (default: drizzle.config.ts)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### migrate
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
--config=<path> Config file path
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### push
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
--strict Require approval before executing SQL
|
|
191
|
+
--verbose Print all SQL before execution
|
|
192
|
+
--force Auto-accept data-loss statements
|
|
193
|
+
--config=<path> Config file path
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### pull
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
--config=<path> Config file path
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### studio
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
--port=<number> Studio port (default: 4983)
|
|
206
|
+
--host=<string> Studio host (default: localhost)
|
|
207
|
+
--config=<path> Config file path
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Global
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
--config=<path> Path to drizzle.config.ts (works with all commands)
|
|
214
|
+
```
|