@netlify/agent-runner-cli 1.29.1 → 1.30.1-broken.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.
@@ -0,0 +1,423 @@
1
+ ---
2
+ name: netlify-database
3
+ description: Use Netlify Database to store application data — records, collections, lists, user data, or any structured information. Provides a fully managed Postgres database. Use for CRUD operations, queryable data, or anything you would naturally model as rows in a table. Use Blobs only for files, binary assets, or cached responses.
4
+ ---
5
+
6
+ # Netlify Database
7
+
8
+ Netlify Database 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/database` package.
10
+
11
+ Prefer Netlify Database over Netlify Blobs for any application data — records, collections, lists, user-generated content,
12
+ or anything you would store as rows. Use Blobs only for files, binary assets (images, video), or cached responses.
13
+
14
+ ## CRITICAL: Use the `@beta` dist-tag for Drizzle
15
+
16
+ The Netlify Database adapter for Drizzle ORM only exists on the `beta` release line of `drizzle-orm`. You MUST install
17
+ both `drizzle-orm` and `drizzle-kit` from the `@beta` dist-tag — the default `latest` versions do not include the
18
+ Netlify Database adapter and will fail.
19
+
20
+ This affects:
21
+ - `npm install` commands: always install `drizzle-orm@beta` and `drizzle-kit@beta` (never `drizzle-orm` or
22
+ `drizzle-kit` without the tag).
23
+ - Import paths: use `drizzle-orm`, `drizzle-orm/pg-core`, and `drizzle-orm/netlify-db` as normal. No package
24
+ name rewriting is needed — the `@beta` tag only affects the installed version, not the imports.
25
+
26
+ If you forget the `@beta` tag, `drizzle-kit generate` will error with a message about being outdated, or the
27
+ `drizzle-orm/netlify-db` import will fail to resolve.
28
+
29
+ ## CRITICAL: Database schema changes always require migrations
30
+
31
+ Any code change that assumes a different database schema (adding tables, adding columns, renaming fields, changing types,
32
+ adding indexes, etc.) will only work if a corresponding migration exists in `netlify/database/migrations/`. Without a
33
+ migration, the database schema will not change and the application will break at runtime.
34
+
35
+ This applies regardless of whether you are using Drizzle ORM or the native `@netlify/database` driver:
36
+ - **With Drizzle ORM**: Update the schema definition, then run `npx drizzle-kit generate`
37
+ - **With native driver**: Write a new SQL migration file in `netlify/database/migrations/`
38
+
39
+ Never consider a schema change complete without a corresponding migration file.
40
+
41
+ ## Setup with Drizzle ORM (recommended)
42
+
43
+ This is the recommended approach. It gives you type-safe schema definitions, a query builder, and automatic migration
44
+ generation.
45
+
46
+ ### Step 1: Install packages
47
+
48
+ ```bash
49
+ npm install @netlify/database drizzle-orm@beta
50
+ npm install -D drizzle-kit@beta
51
+ ```
52
+
53
+ ### Step 2: Create the schema file
54
+
55
+ Create `db/schema.ts` at the project root. Define all your tables here using Drizzle's schema builder.
56
+
57
+ ```typescript
58
+ // db/schema.ts
59
+ import { pgTable, serial, text, timestamp, integer } from "drizzle-orm/pg-core";
60
+
61
+ export const users = pgTable("users", {
62
+ id: serial().primaryKey(),
63
+ name: text().notNull(),
64
+ email: text().notNull().unique(),
65
+ createdAt: timestamp("created_at").defaultNow(),
66
+ });
67
+ ```
68
+
69
+ When adding tables with foreign keys:
70
+
71
+ ```typescript
72
+ export const posts = pgTable("posts", {
73
+ id: serial().primaryKey(),
74
+ title: text().notNull(),
75
+ content: text().notNull().default(""),
76
+ authorId: integer("author_id").notNull().references(() => users.id),
77
+ createdAt: timestamp("created_at").defaultNow(),
78
+ });
79
+ ```
80
+
81
+ IMPORTANT: Use snake_case strings for column names (e.g. `"created_at"`, `"author_id"`) to match Postgres conventions.
82
+ The Drizzle column variable names can be camelCase.
83
+
84
+ ### Step 3: Create the database client
85
+
86
+ Create `db/index.ts`. This file initializes the Drizzle client with the native Netlify Database adapter.
87
+
88
+ ```typescript
89
+ // db/index.ts
90
+ import { drizzle } from "drizzle-orm/netlify-db";
91
+ import * as schema from "./schema.js";
92
+
93
+ export const db = drizzle({ schema });
94
+ ```
95
+
96
+ The connection is configured automatically — no connection string needed.
97
+
98
+ ### Step 4: Configure Drizzle Kit
99
+
100
+ Create `drizzle.config.ts` at the project root. The `out` property MUST be set to `netlify/database/migrations` for
101
+ Netlify to automatically apply migrations.
102
+
103
+ ```typescript
104
+ // drizzle.config.ts
105
+ import { defineConfig } from "drizzle-kit";
106
+
107
+ export default defineConfig({
108
+ dialect: "postgresql",
109
+ schema: "./db/schema.ts",
110
+ out: "netlify/database/migrations",
111
+ });
112
+ ```
113
+
114
+ ### Step 5: Generate migrations
115
+
116
+ Every time you change the schema, run `drizzle-kit generate`:
117
+
118
+ ```bash
119
+ npx drizzle-kit generate
120
+ ```
121
+
122
+ This creates SQL migration files in `netlify/database/migrations/`.
123
+
124
+ ### Step 6: Use the database in functions
125
+
126
+ ```typescript
127
+ // netlify/functions/api.ts
128
+ import type { Config } from "@netlify/functions";
129
+ import { db } from "../../db/index.js";
130
+ import { users } from "../../db/schema.js";
131
+
132
+ export default async (req: Request) => {
133
+ if (req.method === "GET") {
134
+ const allUsers = await db.select().from(users);
135
+ return Response.json(allUsers);
136
+ }
137
+
138
+ if (req.method === "POST") {
139
+ const { name, email } = await req.json();
140
+ const [user] = await db.insert(users).values({ name, email }).returning();
141
+ return Response.json(user, { status: 201 });
142
+ }
143
+
144
+ return new Response("Method not allowed", { status: 405 });
145
+ };
146
+
147
+ export const config: Config = {
148
+ path: "/api/users",
149
+ };
150
+ ```
151
+
152
+ ### Drizzle ORM query reference
153
+
154
+ ```typescript
155
+ import { eq, desc, and, or, like, sql } from "drizzle-orm";
156
+ import { db } from "./db/index.js";
157
+ import { users, posts } from "./db/schema.js";
158
+
159
+ // Select all
160
+ const allUsers = await db.select().from(users);
161
+
162
+ // Select with condition
163
+ const user = await db.select().from(users).where(eq(users.id, 1));
164
+
165
+ // Select with ordering
166
+ const sorted = await db.select().from(users).orderBy(desc(users.createdAt));
167
+
168
+ // Select with limit
169
+ const first10 = await db.select().from(users).limit(10);
170
+
171
+ // Select specific columns
172
+ const names = await db.select({ name: users.name }).from(users);
173
+
174
+ // Insert one row
175
+ const [inserted] = await db.insert(users).values({ name: "Ada", email: "ada@example.com" }).returning();
176
+
177
+ // Insert multiple rows
178
+ await db.insert(users).values([
179
+ { name: "Ada", email: "ada@example.com" },
180
+ { name: "Bob", email: "bob@example.com" },
181
+ ]);
182
+
183
+ // Update
184
+ await db.update(users).set({ name: "Ada Lovelace" }).where(eq(users.id, 1));
185
+
186
+ // Delete
187
+ await db.delete(users).where(eq(users.id, 1));
188
+
189
+ // Join
190
+ const postsWithAuthors = await db
191
+ .select()
192
+ .from(posts)
193
+ .innerJoin(users, eq(posts.authorId, users.id));
194
+ ```
195
+
196
+ ## Setup with native driver (alternative)
197
+
198
+ Use this when you prefer raw SQL or don't want Drizzle ORM.
199
+
200
+ ### Install
201
+
202
+ ```bash
203
+ npm install @netlify/database
204
+ ```
205
+
206
+ ### Query with `db.sql`
207
+
208
+ ```typescript
209
+ import { getDatabase } from "@netlify/database";
210
+
211
+ const db = getDatabase();
212
+
213
+ // Select
214
+ const users = await db.sql`SELECT * FROM users`;
215
+
216
+ // Select with parameters (automatically parameterized)
217
+ const user = await db.sql`SELECT * FROM users WHERE id = ${userId}`;
218
+
219
+ // Insert with RETURNING
220
+ const [newUser] = await db.sql`
221
+ INSERT INTO users (name, email)
222
+ VALUES (${"Ada"}, ${"ada@example.com"})
223
+ RETURNING *
224
+ `;
225
+
226
+ // Update
227
+ await db.sql`UPDATE users SET name = ${"Ada Lovelace"} WHERE id = ${1}`;
228
+
229
+ // Delete
230
+ await db.sql`DELETE FROM users WHERE id = ${1}`;
231
+
232
+ // Bulk insert
233
+ const data = db.sql.values([
234
+ ["Ada", "ada@example.com"],
235
+ ["Bob", "bob@example.com"],
236
+ ]);
237
+ await db.sql`INSERT INTO users (name, email) VALUES ${data}`;
238
+ ```
239
+
240
+ ### Transactions with `db.pool`
241
+
242
+ ```typescript
243
+ const db = getDatabase();
244
+
245
+ const client = await db.pool.connect();
246
+ try {
247
+ await client.query("BEGIN");
248
+ await client.query("INSERT INTO users (name, email) VALUES ($1, $2)", ["Ada", "ada@example.com"]);
249
+ await client.query("INSERT INTO posts (author_id, title) VALUES ($1, $2)", [1, "First post"]);
250
+ await client.query("COMMIT");
251
+ } catch (e) {
252
+ await client.query("ROLLBACK");
253
+ throw e;
254
+ } finally {
255
+ client.release();
256
+ }
257
+ ```
258
+
259
+ ### Write migrations manually
260
+
261
+ When using the native driver, write SQL migration files by hand in `netlify/database/migrations/`.
262
+
263
+ Each migration is a SQL file named `<number>_<slug>.sql`:
264
+
265
+ ```
266
+ netlify/database/migrations/
267
+ ├── 20260417143022_create-users.sql
268
+ ├── 20260418091500_add-posts.sql
269
+ └── 20260419112330_create-comments.sql
270
+ ```
271
+
272
+ Example migration:
273
+
274
+ ```sql
275
+ -- netlify/database/migrations/20260417143022_create-users.sql
276
+ CREATE TABLE users (
277
+ id SERIAL PRIMARY KEY,
278
+ name TEXT NOT NULL,
279
+ email TEXT UNIQUE NOT NULL,
280
+ created_at TIMESTAMP DEFAULT NOW()
281
+ );
282
+ ```
283
+
284
+ Rules for migration names:
285
+ - `number` is any sequence of digits (e.g. `001`, `0001`, or a Unix timestamp)
286
+ - `slug` contains only lowercase letters, numbers, hyphens, and underscores
287
+ - Migrations are sorted lexicographically and applied in order
288
+ - NEVER modify a migration file once it has been applied to any database (local, preview, or production).
289
+ Unapplied migration files can be freely deleted and regenerated; see "Iterating on migrations within a
290
+ session" below.
291
+
292
+ ## Migrations: how they work
293
+
294
+ IMPORTANT: You must NEVER apply migrations yourself. Your job is only to CREATE migration files — either by running
295
+ `npx drizzle-kit generate` (when using Drizzle ORM) or by writing SQL files in `netlify/database/migrations/` (when using
296
+ the native driver). The Netlify platform applies migrations automatically at the right point in the deploy lifecycle.
297
+ Do NOT run `drizzle-kit migrate`, `drizzle-kit push`, or execute migration SQL directly against the database.
298
+
299
+ Netlify applies migrations automatically during deploys:
300
+
301
+ - **Production deploys:** Applied immediately before the deploy is published. A failure blocks publishing.
302
+ - **Deploy previews:** Applied on every new deploy, immediately before it becomes available. A failure fails the deploy.
303
+
304
+ Migrations MUST be in `netlify/database/migrations/` to be applied automatically.
305
+
306
+ Two formats are supported:
307
+ 1. SQL files directly in the directory (e.g. `20260417143022_create-users.sql`)
308
+ 2. Subdirectories containing a `migration.sql` file (e.g. `20260417143022_create-users/migration.sql`)
309
+
310
+ ## Database branches
311
+
312
+ - **Production deploys** access the main database only.
313
+ - **Deploy previews** get their own isolated database branch with a copy of production data.
314
+ - Changes in a deploy preview's branch never affect the production database.
315
+ - You do NOT need to do anything to enable branching — it is automatic.
316
+ - A preview branch persists across multiple runs in the same session. Migrations you applied in an earlier run
317
+ remain applied when a later run starts, and any test data you inserted persists too. This matters when iterating
318
+ on migrations — see "Iterating on migrations within a session" below.
319
+
320
+ ## Inspecting migration state
321
+
322
+ The current database state — which migrations are already applied to your preview branch, and which migration
323
+ files exist on disk but haven't been applied yet — is included in your context at the start of the run, under
324
+ the "Current Netlify Database state" heading. Read that list before planning any migration work. You do not
325
+ need to run `netlify db status` at the start of a task to get this information.
326
+
327
+ If you need to re-check state mid-session — typically after generating a new migration, applying migrations, or
328
+ running `netlify db migrations reset` — run:
329
+
330
+ ```bash
331
+ netlify db status
332
+ ```
333
+
334
+ The command targets your preview database branch automatically (`NETLIFY_DB_BRANCH` is pre-set).
335
+
336
+ It reports:
337
+ - **Applied**: migrations that have been applied to the branch
338
+ - **Pending**: migration files that exist locally but have not been applied
339
+ - **Missing on disk**: migrations applied on the server but not present locally (drift — this is a serious
340
+ state; never intentionally create it)
341
+ - **Out of order**: pending migrations whose prefix sorts before an already-applied migration. The platform
342
+ rejects these, so fix via the "Rule of thumb" below (reset + regenerate). If `drizzle-kit generate` then
343
+ reports no changes, the out-of-order files were redundant and deleting them was the complete fix.
344
+
345
+ ## Iterating on migrations within a session
346
+
347
+ A single agent session can run multiple times, and all runs share one preview database branch. When you realize
348
+ a migration from an earlier run needs to change, the correct action depends on whether the migration has been
349
+ applied to the database yet.
350
+
351
+ ### Rule of thumb
352
+
353
+ - If a migration has been **applied** to any database (local dev DB, preview branch, or production), treat it
354
+ as immutable. Roll forward with a new migration that applies the correction. This is always safe.
355
+ - If a migration is **not yet applied** anywhere (it's only on disk), don't edit it in place. Run
356
+ `netlify db migrations reset` to delete it, update `db/schema.ts` to reflect your new intent, then run
357
+ `npx drizzle-kit generate` to produce a fresh migration. Editing SQL or snapshot files directly can
358
+ desync Drizzle Kit's internal state and produce broken migrations on the next generate.
359
+
360
+ ### `netlify db migrations reset` — cleaning up unapplied migrations
361
+
362
+ `netlify db migrations reset` deletes local migration files that have **not yet been applied** to your preview
363
+ branch. Applied migrations and their data are left alone.
364
+
365
+ ```bash
366
+ netlify db migrations reset
367
+ ```
368
+
369
+ Typical use: after a rebase pulls in new migrations from main, you may have locally-generated migrations that
370
+ now conflict with or duplicate main's work. Running `netlify db migrations reset` clears those unapplied files
371
+ so you can regenerate cleanly.
372
+
373
+ The command is safe — it cannot remove migrations that have been applied, so it cannot produce a broken
374
+ database state.
375
+
376
+ ## Connecting to the database
377
+
378
+ Use `netlify db connect` to query the database directly for troubleshooting or inspecting the current schema. Always use
379
+ the `--query` flag for one-shot execution — do NOT use interactive mode.
380
+
381
+ ```bash
382
+ # Inspect the current schema
383
+ netlify db connect --query "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"
384
+
385
+ # Check columns on a table
386
+ netlify db connect --query "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'users'"
387
+
388
+ # Read data
389
+ netlify db connect --query "SELECT * FROM users LIMIT 10"
390
+
391
+ # Get results as JSON
392
+ netlify db connect --query "SELECT * FROM users LIMIT 10" --json
393
+ ```
394
+
395
+ The database connection is read-only. You can use it to inspect the schema and read data, but you cannot insert, update,
396
+ or delete rows.
397
+
398
+ **CRITICAL: NEVER run DDL statements (CREATE, ALTER, DROP, TRUNCATE) or any other schema-mutating queries through
399
+ `netlify db connect`.** The only supported way to change the database schema is through migration files in
400
+ `netlify/database/migrations/`. Running DDL directly will cause the migration state to diverge from the actual schema,
401
+ leading to unpredictable and broken behavior.
402
+
403
+ ## Common mistakes to avoid
404
+
405
+ 1. **Wrong migration output directory**: Drizzle Kit defaults to `drizzle/`. You MUST set
406
+ `out: "netlify/database/migrations"` in `drizzle.config.ts`. If migrations are in the wrong directory, they will not
407
+ be applied.
408
+
409
+ 2. **Forgetting the `@beta` dist-tag**: The Netlify Database adapter only exists on `drizzle-orm@beta` and
410
+ `drizzle-kit@beta`. Installing `drizzle-orm` or `drizzle-kit` without the `@beta` tag pulls the `latest` releases,
411
+ which lack the adapter and will fail. Always install with `drizzle-orm@beta` and `drizzle-kit@beta`.
412
+
413
+ 3. **Missing `.js` extension in imports**: When using TypeScript with ES modules, import paths should include the `.js`
414
+ extension (e.g. `from "./schema.js"`, `from "../../db/index.js"`).
415
+
416
+ 4. **Creating tables with raw SQL when using Drizzle**: If you use Drizzle ORM, define all tables in `db/schema.ts`
417
+ and generate migrations with `drizzle-kit generate`. Do not write raw `CREATE TABLE` SQL — the schema file is the
418
+ source of truth.
419
+
420
+ 5. **Misunderstanding `netlify db migrations reset`**: This command only deletes migration files that have
421
+ **not yet been applied** to your preview branch. It cannot undo an already-applied migration — to change
422
+ something that's already applied, you must roll forward with a new migration.
423
+