SQLite or Postgres

Rows stay in memory until you name a database, and the boot warns you so. One entry in sources: puts them in SQLite or Postgres, and fougere migrate writes the tables.

Name the database

Install the adapter and the driver of your engine:

npm install @fougere/adapter-sql better-sqlite3   # SQLite — pg, mysql2, or tedious tarn for the others

Then name the database:

// fougere.config.ts
import '@fougere/adapter-sql';

export default defineFougere({
  sources: {
    main: { type: 'sqlite', path: '.data/app.db' },
  },
});

The entry with no rows: list holds every entity. Your handlers do not change: where rows live is a fact about the app, not about the code that uses them.

type: names the engine, and the keys beside it belong to that engine:

typeSaysImported from
sqlitepath@fougere/adapter-sql
pg, mysql, mssqlurl@fougere/adapter-sql
d1binding — the Worker's D1 binding@fougere/adapter-sql
filepath, format — one JSON or YAML file per row@fougere/adapter-file
memorynothing — a Map@fougere/adapter-memory
envnothing — the environment, read only@fougere/adapter-env

Every key of every type, read off the code: Source types.

The import is what makes the word exist. Without it, the editor refuses the entry and the boot answers Unknown source type … To add one, import the adapter that provides it. A misspelled key — pathh — is refused by both, by name, and so is { type: 'pg' } without a url.

Drivers

@fougere/adapter-sql loads on any runtime — Node, Bun, Deno, a Worker — and an engine loads its driver only when it opens: better-sqlite3 for SQLite, pg, mysql2, or tedious and tarn, installed by your app. A missing driver is refused with the command that adds it. Kysely comes with the adapter.

  • Under Bun, type: 'sqlite' opens with bun:sqlite, since better-sqlite3 does not load there.
  • On a Worker, type: 'd1' reads its binding from cloudflare:workers at the first query.

Create the tables

The boot never writes the schema. It compares the database with the entities and refuses to start when the database is behind, naming what is missing:

Fougere boot refused: the database is missing 1 change(s) from the entities:
  products — no table
  On your machine: fougere migrate --latest --apply
  On a shared database: fougere freeze, then fougere migrate --apply

On your machine, fougere migrate --latest --apply creates the tables. Keep it as a script beside the others — "migrate": "fougere migrate --latest --apply" in package.json — and run it again each time an entity gains a field; the boot tells you when.

Change a field

A rename is declared on the entity, and the rows follow:

export default class Product extends entity(
  { id: primary(), name: text(), cents: number({ min: 0 }) },
  { previous: { name: 'title' } },
) {}

npm run migrate renames the column on your database. Before the change leaves your machine, freeze it:

fougere freeze v2

A deployment runs fougere migrate --apply, which applies only what was frozen — a change nobody froze is refused there, by name. A field removed without previous: is dropped: fougere freeze warns that its column, and its data, go at the next migrate. The whole story: Evolve a schema.

More than one database

Each further entry lists the entities it holds in rows:; the one without rows: keeps the rest:

sources: {
  main:    { type: 'sqlite', path: '.data/app.db' },
  archive: { type: 'file', path: './rows', rows: ['Snapshot'] },   // import '@fougere/adapter-file'
  legacy:  { type: 'pg', url: process.env.DATABASE_URL, rows: ['Book'] },
},
  • Two entries without rows: refuse the boot: nothing would say which holds the rest.
  • When every entry lists its rows, an entity left out of all of them refuses the boot, by name.
  • An entry may list values: — entities kept as one value each: see Values. A source nothing is written to, such as env, never holds the rest.
  • A view gets no table unless it says .anchor(), on the entity — never in this file.

Querying across databases, and two fronds sharing a table: Several databases.

Built with Fougere and Nuxt, hosted on GitHub Pages.