@netlify/agent-runner-cli 1.93.1 → 1.94.0-netlifydb.1
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/dist/bin-local.js +35 -34
- package/dist/bin.js +34 -33
- package/dist/index.js +33 -32
- package/dist/skills/general-database/SKILL.md +29 -15
- package/dist/skills/netlify-blobs/SKILL.md +2 -2
- package/dist/skills/netlify-db/SKILL.md +339 -0
- package/package.json +1 -1
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: netlify-db
|
|
3
|
+
description: Use Netlify DB for relational data storage with a fully managed Postgres database. Use when implementing data models with tables, relationships, SQL queries, transactions, or any data that benefits from a relational schema.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Netlify DB
|
|
7
|
+
|
|
8
|
+
Netlify DB is a fully managed Postgres database built into the Netlify platform. It handles provisioning, migrations,
|
|
9
|
+
and database branching automatically. There is no setup beyond installing the `@netlify/db` package.
|
|
10
|
+
|
|
11
|
+
Prefer Netlify DB over Netlify Blobs when the data is relational or requires SQL queries, unless the user explicitly
|
|
12
|
+
asks for a different storage solution.
|
|
13
|
+
|
|
14
|
+
## CRITICAL: Temporary package name
|
|
15
|
+
|
|
16
|
+
The Drizzle ORM Netlify DB adapter has not yet been published to the main `drizzle-orm` package. Until it is, you MUST
|
|
17
|
+
use the `netlify-drizzle-orm-tests` package instead of `drizzle-orm` for ALL Drizzle imports.
|
|
18
|
+
|
|
19
|
+
This affects:
|
|
20
|
+
- `npm install` commands: use `netlify-drizzle-orm-tests` instead of `drizzle-orm`
|
|
21
|
+
- ALL import statements: use `netlify-drizzle-orm-tests` and `netlify-drizzle-orm-tests/pg-core` and
|
|
22
|
+
`netlify-drizzle-orm-tests/netlify-db` instead of the `drizzle-orm` equivalents
|
|
23
|
+
|
|
24
|
+
The `drizzle-kit` package is NOT affected — use the regular `drizzle-kit` package.
|
|
25
|
+
|
|
26
|
+
## Setup with Drizzle ORM (recommended)
|
|
27
|
+
|
|
28
|
+
This is the recommended approach. It gives you type-safe schema definitions, a query builder, and automatic migration
|
|
29
|
+
generation.
|
|
30
|
+
|
|
31
|
+
### Step 1: Install packages
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @netlify/db netlify-drizzle-orm-tests
|
|
35
|
+
npm install -D drizzle-kit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Step 2: Create the schema file
|
|
39
|
+
|
|
40
|
+
Create `db/schema.ts` at the project root. Define all your tables here using Drizzle's schema builder.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// db/schema.ts
|
|
44
|
+
import { pgTable, serial, text, timestamp, integer } from "netlify-drizzle-orm-tests/pg-core";
|
|
45
|
+
|
|
46
|
+
export const users = pgTable("users", {
|
|
47
|
+
id: serial().primaryKey(),
|
|
48
|
+
name: text().notNull(),
|
|
49
|
+
email: text().notNull().unique(),
|
|
50
|
+
createdAt: timestamp("created_at").defaultNow(),
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
When adding tables with foreign keys:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
export const posts = pgTable("posts", {
|
|
58
|
+
id: serial().primaryKey(),
|
|
59
|
+
title: text().notNull(),
|
|
60
|
+
content: text().notNull().default(""),
|
|
61
|
+
authorId: integer("author_id").notNull().references(() => users.id),
|
|
62
|
+
createdAt: timestamp("created_at").defaultNow(),
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
IMPORTANT: Use snake_case strings for column names (e.g. `"created_at"`, `"author_id"`) to match Postgres conventions.
|
|
67
|
+
The Drizzle column variable names can be camelCase.
|
|
68
|
+
|
|
69
|
+
### Step 3: Create the database client
|
|
70
|
+
|
|
71
|
+
Create `db/index.ts`. This file initializes the Drizzle client with the native Netlify DB adapter.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// db/index.ts
|
|
75
|
+
import { drizzle } from "netlify-drizzle-orm-tests/netlify-db";
|
|
76
|
+
import * as schema from "./schema.js";
|
|
77
|
+
|
|
78
|
+
export const db = drizzle({ schema });
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The connection is configured automatically — no connection string needed.
|
|
82
|
+
|
|
83
|
+
### Step 4: Configure Drizzle Kit
|
|
84
|
+
|
|
85
|
+
Create `drizzle.config.ts` at the project root. The `out` property MUST be set to `netlify/db/migrations` for Netlify
|
|
86
|
+
to automatically apply migrations.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// drizzle.config.ts
|
|
90
|
+
import { defineConfig } from "drizzle-kit";
|
|
91
|
+
|
|
92
|
+
export default defineConfig({
|
|
93
|
+
dialect: "postgresql",
|
|
94
|
+
schema: "./db/schema.ts",
|
|
95
|
+
out: "netlify/db/migrations",
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Alternatively, use the helper:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { defineConfig } from "drizzle-kit";
|
|
103
|
+
import { withNetlifyDB } from "@netlify/db/drizzle";
|
|
104
|
+
|
|
105
|
+
export default defineConfig({
|
|
106
|
+
dialect: "postgresql",
|
|
107
|
+
schema: "./db/schema.ts",
|
|
108
|
+
...withNetlifyDB(),
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Step 5: Generate migrations
|
|
113
|
+
|
|
114
|
+
Run this every time you change the schema:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npx drizzle-kit generate
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This creates SQL migration files in `netlify/db/migrations/`. DO NOT manually edit the generated migration files.
|
|
121
|
+
DO NOT create the migrations directory manually — `drizzle-kit generate` creates it.
|
|
122
|
+
|
|
123
|
+
### Step 6: Use the database in functions
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// netlify/functions/api.ts
|
|
127
|
+
import type { Config } from "@netlify/functions";
|
|
128
|
+
import { db } from "../../db/index.js";
|
|
129
|
+
import { users } from "../../db/schema.js";
|
|
130
|
+
|
|
131
|
+
export default async (req: Request) => {
|
|
132
|
+
if (req.method === "GET") {
|
|
133
|
+
const allUsers = await db.select().from(users);
|
|
134
|
+
return Response.json(allUsers);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (req.method === "POST") {
|
|
138
|
+
const { name, email } = await req.json();
|
|
139
|
+
const [user] = await db.insert(users).values({ name, email }).returning();
|
|
140
|
+
return Response.json(user, { status: 201 });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return new Response("Method not allowed", { status: 405 });
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const config: Config = {
|
|
147
|
+
path: "/api/users",
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Drizzle ORM query reference
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { eq, desc, and, or, like, sql } from "netlify-drizzle-orm-tests";
|
|
155
|
+
import { db } from "./db/index.js";
|
|
156
|
+
import { users, posts } from "./db/schema.js";
|
|
157
|
+
|
|
158
|
+
// Select all
|
|
159
|
+
const allUsers = await db.select().from(users);
|
|
160
|
+
|
|
161
|
+
// Select with condition
|
|
162
|
+
const user = await db.select().from(users).where(eq(users.id, 1));
|
|
163
|
+
|
|
164
|
+
// Select with ordering
|
|
165
|
+
const sorted = await db.select().from(users).orderBy(desc(users.createdAt));
|
|
166
|
+
|
|
167
|
+
// Select with limit
|
|
168
|
+
const first10 = await db.select().from(users).limit(10);
|
|
169
|
+
|
|
170
|
+
// Select specific columns
|
|
171
|
+
const names = await db.select({ name: users.name }).from(users);
|
|
172
|
+
|
|
173
|
+
// Insert one row
|
|
174
|
+
const [inserted] = await db.insert(users).values({ name: "Ada", email: "ada@example.com" }).returning();
|
|
175
|
+
|
|
176
|
+
// Insert multiple rows
|
|
177
|
+
await db.insert(users).values([
|
|
178
|
+
{ name: "Ada", email: "ada@example.com" },
|
|
179
|
+
{ name: "Bob", email: "bob@example.com" },
|
|
180
|
+
]);
|
|
181
|
+
|
|
182
|
+
// Update
|
|
183
|
+
await db.update(users).set({ name: "Ada Lovelace" }).where(eq(users.id, 1));
|
|
184
|
+
|
|
185
|
+
// Delete
|
|
186
|
+
await db.delete(users).where(eq(users.id, 1));
|
|
187
|
+
|
|
188
|
+
// Join
|
|
189
|
+
const postsWithAuthors = await db
|
|
190
|
+
.select()
|
|
191
|
+
.from(posts)
|
|
192
|
+
.innerJoin(users, eq(posts.authorId, users.id));
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Setup with native driver (alternative)
|
|
196
|
+
|
|
197
|
+
Use this when you prefer raw SQL or don't want Drizzle ORM.
|
|
198
|
+
|
|
199
|
+
### Install
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm install @netlify/db
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Query with `db.sql`
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { getDatabase } from "@netlify/db";
|
|
209
|
+
|
|
210
|
+
const db = getDatabase();
|
|
211
|
+
|
|
212
|
+
// Select
|
|
213
|
+
const users = await db.sql`SELECT * FROM users`;
|
|
214
|
+
|
|
215
|
+
// Select with parameters (automatically parameterized)
|
|
216
|
+
const user = await db.sql`SELECT * FROM users WHERE id = ${userId}`;
|
|
217
|
+
|
|
218
|
+
// Insert with RETURNING
|
|
219
|
+
const [newUser] = await db.sql`
|
|
220
|
+
INSERT INTO users (name, email)
|
|
221
|
+
VALUES (${"Ada"}, ${"ada@example.com"})
|
|
222
|
+
RETURNING *
|
|
223
|
+
`;
|
|
224
|
+
|
|
225
|
+
// Update
|
|
226
|
+
await db.sql`UPDATE users SET name = ${"Ada Lovelace"} WHERE id = ${1}`;
|
|
227
|
+
|
|
228
|
+
// Delete
|
|
229
|
+
await db.sql`DELETE FROM users WHERE id = ${1}`;
|
|
230
|
+
|
|
231
|
+
// Bulk insert
|
|
232
|
+
const data = db.sql.values([
|
|
233
|
+
["Ada", "ada@example.com"],
|
|
234
|
+
["Bob", "bob@example.com"],
|
|
235
|
+
]);
|
|
236
|
+
await db.sql`INSERT INTO users (name, email) VALUES ${data}`;
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Transactions with `db.pool`
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const db = getDatabase();
|
|
243
|
+
|
|
244
|
+
const client = await db.pool.connect();
|
|
245
|
+
try {
|
|
246
|
+
await client.query("BEGIN");
|
|
247
|
+
await client.query("INSERT INTO users (name, email) VALUES ($1, $2)", ["Ada", "ada@example.com"]);
|
|
248
|
+
await client.query("INSERT INTO posts (author_id, title) VALUES ($1, $2)", [1, "First post"]);
|
|
249
|
+
await client.query("COMMIT");
|
|
250
|
+
} catch (e) {
|
|
251
|
+
await client.query("ROLLBACK");
|
|
252
|
+
throw e;
|
|
253
|
+
} finally {
|
|
254
|
+
client.release();
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Write migrations manually
|
|
259
|
+
|
|
260
|
+
When using the native driver, write SQL migration files by hand in `netlify/db/migrations/`.
|
|
261
|
+
|
|
262
|
+
Each migration is a SQL file named `<number>_<slug>.sql`:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
netlify/db/migrations/
|
|
266
|
+
├── 001_create-users.sql
|
|
267
|
+
├── 002_add-posts.sql
|
|
268
|
+
└── 003_create-comments.sql
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Example migration:
|
|
272
|
+
|
|
273
|
+
```sql
|
|
274
|
+
-- netlify/db/migrations/001_create-users.sql
|
|
275
|
+
CREATE TABLE users (
|
|
276
|
+
id SERIAL PRIMARY KEY,
|
|
277
|
+
name TEXT NOT NULL,
|
|
278
|
+
email TEXT UNIQUE NOT NULL,
|
|
279
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
280
|
+
);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Rules for migration names:
|
|
284
|
+
- `number` is any sequence of digits (e.g. `001`, `0001`, or a Unix timestamp)
|
|
285
|
+
- `slug` contains only lowercase letters, numbers, hyphens, and underscores
|
|
286
|
+
- Migrations are sorted lexicographically and applied in order
|
|
287
|
+
- NEVER modify a migration that has already been applied — create a new one instead
|
|
288
|
+
|
|
289
|
+
## Migrations: how they work
|
|
290
|
+
|
|
291
|
+
IMPORTANT: You must NEVER apply migrations yourself. Your job is only to CREATE migration files — either by running
|
|
292
|
+
`npx drizzle-kit generate` (when using Drizzle ORM) or by writing SQL files in `netlify/db/migrations/` (when using
|
|
293
|
+
the native driver). The Netlify platform applies migrations automatically at the right point in the deploy lifecycle.
|
|
294
|
+
Do NOT run `drizzle-kit migrate`, `drizzle-kit push`, or execute migration SQL directly against the database.
|
|
295
|
+
|
|
296
|
+
Netlify applies migrations automatically during deploys:
|
|
297
|
+
|
|
298
|
+
- **Production deploys:** Applied immediately before the deploy is published. A failure blocks publishing.
|
|
299
|
+
- **Deploy previews:** Applied on every new deploy, immediately before it becomes available. A failure fails the deploy.
|
|
300
|
+
|
|
301
|
+
Migrations MUST be in `netlify/db/migrations/` to be applied automatically.
|
|
302
|
+
|
|
303
|
+
Two formats are supported:
|
|
304
|
+
1. SQL files directly in the directory (e.g. `001_create-users.sql`)
|
|
305
|
+
2. Subdirectories containing a `migration.sql` file (e.g. `001_create-users/migration.sql`)
|
|
306
|
+
|
|
307
|
+
Drizzle Kit generates migrations in the subdirectory format. Both formats can coexist.
|
|
308
|
+
|
|
309
|
+
## Database branches
|
|
310
|
+
|
|
311
|
+
- **Production deploys** access the main database only.
|
|
312
|
+
- **Deploy previews** get their own isolated database branch with a copy of production data.
|
|
313
|
+
- Changes in a deploy preview's branch never affect the production database.
|
|
314
|
+
- You do NOT need to do anything to enable branching — it is automatic.
|
|
315
|
+
|
|
316
|
+
## Common mistakes to avoid
|
|
317
|
+
|
|
318
|
+
1. **Wrong migration output directory**: Drizzle Kit defaults to `drizzle/`. You MUST set `out: "netlify/db/migrations"`
|
|
319
|
+
in `drizzle.config.ts` or use `...withNetlifyDB()`. If migrations are in the wrong directory, they will not be
|
|
320
|
+
applied.
|
|
321
|
+
|
|
322
|
+
2. **Using `drizzle-orm` instead of `netlify-drizzle-orm-tests`**: The Netlify DB adapter is not yet in the main
|
|
323
|
+
`drizzle-orm` package. Use `netlify-drizzle-orm-tests` for all Drizzle imports.
|
|
324
|
+
|
|
325
|
+
3. **Forgetting to run `drizzle-kit generate`**: After changing `db/schema.ts`, you must run `npx drizzle-kit generate`
|
|
326
|
+
to create migration files. Schema changes alone do nothing — the migration files are what Netlify applies.
|
|
327
|
+
|
|
328
|
+
4. **Editing generated migrations**: Never manually edit migrations created by Drizzle Kit. If you need to change
|
|
329
|
+
something, update the schema and generate a new migration.
|
|
330
|
+
|
|
331
|
+
5. **Missing `.js` extension in imports**: When using TypeScript with ES modules, import paths should include the `.js`
|
|
332
|
+
extension (e.g. `from "./schema.js"`, `from "../../db/index.js"`).
|
|
333
|
+
|
|
334
|
+
6. **Creating tables with raw SQL when using Drizzle**: If you use Drizzle ORM, define all tables in `db/schema.ts`
|
|
335
|
+
and generate migrations with `drizzle-kit generate`. Do not write raw `CREATE TABLE` SQL — the schema file is the
|
|
336
|
+
source of truth.
|
|
337
|
+
|
|
338
|
+
7. **Applying migrations manually**: NEVER run `drizzle-kit migrate`, `drizzle-kit push`, or execute migration SQL
|
|
339
|
+
against the database. Only create migration files — the Netlify platform applies them automatically during deploys.
|