199 lines
6.7 KiB
Markdown
199 lines
6.7 KiB
Markdown
---
|
|
title: "Database — SQLite"
|
|
aliases: [sqlite-adapter, payload-sqlite, db-sqlite]
|
|
tags: [payloadcms, database, sqlite, drizzle, libsql, turso, cloudflare-d1]
|
|
sources: [raw/database__sqlite.md]
|
|
created: 2026-05-15
|
|
updated: 2026-05-15
|
|
---
|
|
|
|
## Overview
|
|
|
|
Payload supports SQLite via `@payloadcms/db-sqlite`, built on **Drizzle ORM** + **libSQL**. Auto-manages schema changes in dev mode; full migration controls for other environments.
|
|
|
|
```ts
|
|
import { sqliteAdapter } from '@payloadcms/db-sqlite'
|
|
|
|
export default buildConfig({
|
|
db: sqliteAdapter({
|
|
client: {
|
|
url: process.env.DATABASE_URL,
|
|
authToken: process.env.DATABASE_AUTH_TOKEN, // for Turso
|
|
},
|
|
}),
|
|
})
|
|
```
|
|
|
|
## Key Options
|
|
|
|
| Option | Default | Notes |
|
|
|--------|---------|-------|
|
|
| `client` * | — | `{ url, authToken }` passed to `createClient` from `@libsql/client` |
|
|
| `push` | `true` (dev only) | Disable Drizzle db push in development |
|
|
| `migrationDir` | — | Custom migration directory |
|
|
| `idType` | `'number'` | `'number'` or `'uuid'` |
|
|
| `transactionOptions` | — | `SQLiteTransactionConfig` or `false` to disable |
|
|
| `localesSuffix` | `'_locales'` | Table name suffix for localized fields |
|
|
| `relationshipsSuffix` | `'_rels'` | Table name suffix for relationships |
|
|
| `versionsSuffix` | `'_v'` | Table name suffix for versions |
|
|
| `autoIncrement` | `false` | SQLite AUTOINCREMENT — prevents ID reuse from deleted rows |
|
|
| `blocksAsJSON` | `false` | Store blocks as JSON instead of relational tables (perf boost for many blocks) |
|
|
| `busyTimeout` | `0` | ms to wait when DB is locked |
|
|
| `wal` | `false` | Enable WAL mode — see [[wiki/payloadcms/database-sqlite#wal-mode\|WAL Mode]] |
|
|
| `beforeSchemaInit` | — | Hook before schema build |
|
|
| `afterSchemaInit` | — | Hook after schema build |
|
|
| `generateSchemaOutputFile` | `src/payload-generated.schema.ts` | Override generated schema path |
|
|
| `allowIDOnCreate` | `false` | Accept `id` in create operations without custom ID field |
|
|
|
|
## Dev Mode — db push vs Migrations
|
|
|
|
- **db push** (default): auto-syncs schema changes to DB, no manual migration needed; warns on data loss
|
|
- **Migrations only**: set `push: false` — use `payload migrate` commands exclusively
|
|
- Do **not** mix push and manual migrate commands
|
|
|
|
See [[wiki/payloadcms/database-migrations|Database Migrations]] for full CLI reference.
|
|
|
|
## Drizzle Direct Access
|
|
|
|
Generate types first:
|
|
```sh
|
|
npx payload generate:db-schema
|
|
```
|
|
|
|
Then query directly:
|
|
```ts
|
|
import { posts } from './payload-generated-schema'
|
|
import { eq, sql, and } from '@payloadcms/db-sqlite/drizzle'
|
|
|
|
// Relational API
|
|
const posts = await payload.db.drizzle.query.posts.findMany()
|
|
|
|
// Select API
|
|
const result = await payload.db.drizzle
|
|
.select()
|
|
.from(posts)
|
|
.where(and(eq(posts.id, 50), sql`lower(${posts.title}) = 'example post title'`))
|
|
```
|
|
|
|
Exposed on `payload.db`:
|
|
- `payload.db.drizzle` — Drizzle instance
|
|
- `payload.db.tables` — all tables
|
|
- `payload.db.relations` — all relations
|
|
|
|
## Schema Hooks
|
|
|
|
### beforeSchemaInit
|
|
|
|
Runs before schema is built. Use to add tables Payload doesn't manage (e.g. preserving existing DB structure when migrating to Payload).
|
|
|
|
```ts
|
|
sqliteAdapter({
|
|
beforeSchemaInit: [
|
|
({ schema, adapter }) => {
|
|
return {
|
|
...schema,
|
|
tables: {
|
|
...schema.tables,
|
|
addedTable: sqliteTable('added_table', {
|
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
}),
|
|
},
|
|
}
|
|
},
|
|
],
|
|
})
|
|
```
|
|
|
|
Tip: use [Drizzle Introspection](https://orm.drizzle.team/kit-docs/commands#introspect--pull) to generate schema from an existing DB, then import into `beforeSchemaInit`.
|
|
|
|
To include custom tables/columns in `payload generate:db-schema` output, mutate `adapter.rawTables` inside `beforeSchemaInit`:
|
|
```ts
|
|
adapter.rawTables.posts.columns.customColumn = {
|
|
name: 'custom_column',
|
|
type: 'integer',
|
|
notNull: true,
|
|
}
|
|
```
|
|
|
|
### afterSchemaInit
|
|
|
|
Runs after schema is built. Use `extendTable` utility to add columns or composite indexes not in Payload config:
|
|
|
|
```ts
|
|
sqliteAdapter({
|
|
afterSchemaInit: [
|
|
({ schema, extendTable }) => {
|
|
extendTable({
|
|
table: schema.tables.places,
|
|
columns: { extraIntegerColumn: integer('extra_integer_column') },
|
|
extraConfig: (table) => ({
|
|
country_city_idx: index('country_city_idx').on(table.country, table.city),
|
|
}),
|
|
})
|
|
return schema
|
|
},
|
|
],
|
|
})
|
|
```
|
|
|
|
Note: columns/tables added in `afterSchemaInit` won't appear in `generate:db-schema` output — only `beforeSchemaInit` + `rawTables` mutation achieves that.
|
|
|
|
## WAL Mode
|
|
|
|
Write-Ahead Logging improves performance and concurrency:
|
|
|
|
```ts
|
|
db: sqliteAdapter({
|
|
wal: true, // or object with settings
|
|
client: { url: process.env.DATABASE_URL },
|
|
})
|
|
```
|
|
|
|
| WAL Setting | Default | Notes |
|
|
|-------------|---------|-------|
|
|
| `journalSizeLimit` | `67108864` (64MB) | WAL file size before checkpointing |
|
|
| `synchronous` | `'FULL'` | `EXTRA` / `FULL` / `NORMAL` / `OFF` — perf vs safety tradeoff |
|
|
|
|
## Cloudflare D1 (Beta)
|
|
|
|
Serverless SQLite via `@payloadcms/db-d1-sqlite`:
|
|
|
|
```ts
|
|
import { sqliteD1Adapter } from '@payloadcms/db-d1-sqlite'
|
|
|
|
export default buildConfig({
|
|
db: sqliteD1Adapter({
|
|
binding: cloudflare.env.D1,
|
|
readReplicas: 'first-primary', // optional, experimental
|
|
}),
|
|
})
|
|
```
|
|
|
|
- Inherits all SQLite adapter options except `client` (uses `binding` instead)
|
|
- Read replicas: `'first-primary'` strategy — writes always go to primary
|
|
- Must enable read replication in Cloudflare dashboard separately
|
|
|
|
## Key Takeaways
|
|
|
|
- SQLite adapter uses Drizzle ORM + libSQL — supports local files, Turso, and Cloudflare D1
|
|
- **Dev mode**: `push: true` auto-syncs schema — don't mix with manual migrations
|
|
- **Production**: use migrations exclusively (`push: false`)
|
|
- Direct Drizzle access via `payload.db.drizzle` — generate types first with `npx payload generate:db-schema`
|
|
- `beforeSchemaInit` for adding non-Payload tables (e.g. DB migration); `afterSchemaInit` for extending Payload tables
|
|
- Only `rawTables` mutation in `beforeSchemaInit` propagates to generated schema file
|
|
- WAL mode (`wal: true`) recommended for concurrent read-heavy workloads
|
|
- `blocksAsJSON: true` can improve performance when using many blocks
|
|
- `autoIncrement: true` prevents ID reuse — useful for audit/compliance scenarios
|
|
- Cloudflare D1 adapter is beta — same API, `binding` instead of `client`
|
|
|
|
## Sources
|
|
|
|
- `raw/database__sqlite.md` — official Payload CMS SQLite documentation
|
|
|
|
## Related
|
|
|
|
- [[wiki/payloadcms/database-overview|Database — Overview]]
|
|
- [[wiki/payloadcms/database-postgres|Database — Postgres]]
|
|
- [[wiki/payloadcms/database-mongodb|Database — MongoDB]]
|
|
- [[wiki/payloadcms/database-migrations|Database Migrations]]
|
|
- [[wiki/payloadcms/database-indexes|Database Indexes]]
|