Databases for Next.js
Connect @cookieyes/backend to Postgres, SQLite or Cloudflare D1 through the client you already use, and run its migrations.
The backend talks to your database through a client you already have, so it adds no driver of its own and runs wherever your client runs. Pass a store as database.
Postgres
postgresStore() takes any client with a query(text, params) method. With pg:
import { createConsentBackend } from "@cookieyes/backend";
import { postgresStore } from "@cookieyes/backend/postgres";
import { Pool } from "pg";
export const backend = createConsentBackend({
basePath: "/api/consent",
trustedOrigins: ["https://shop.example.com"],
database: postgresStore(new Pool({ connectionString: process.env.DATABASE_URL })),
});Other clients work the same way:
import { postgresStore } from "@cookieyes/backend/postgres";
import { neon } from "@neondatabase/serverless";
import postgres from "postgres";
// Neon's serverless driver, which also runs at the edge.
const neonSql = neon(process.env.DATABASE_URL ?? "");
export const onNeon = postgresStore({ query: (text, params) => neonSql.query(text, params) });
// postgres.js
const sql = postgres(process.env.DATABASE_URL ?? "");
export const onPostgresJs = postgresStore({ query: (text, params) => sql.unsafe(text, params) });Create one client per process and reuse it, so requests share its connections.
SQLite
sqliteStore() takes node:sqlite, better-sqlite3 or bun:sqlite. It needs SQLite 3.35 or later, which all three bundle.
import { DatabaseSync } from "node:sqlite";
import { sqliteStore } from "@cookieyes/backend/sqlite";
export const database = sqliteStore(new DatabaseSync("./data/consent.db"));Keep the file on storage that outlives a deploy. A serverless function's file system is thrown away, and the records with it.
Cloudflare D1
d1Store() takes a D1 binding. See Cloudflare Workers for the whole Worker.
In memory
memoryStore() from @cookieyes/backend/memory keeps records in the process. It is for tests and demos: records are lost on restart, and each instance has its own.
Migrations
The backend creates two tables: cookieyes_consent_records and cookieyes_migrations, which records the migrations already applied. A migration only adds; upgrading the package never asks you to change the schema by hand.
npx cookieyes-backend migrate --config ./lib/consent-backend.ts # shows what would run
npx cookieyes-backend migrate --config ./lib/consent-backend.ts --apply # runs it--config names a module that exports your backend, as its default export or as backend. You can also migrate from code, for example in a deploy step:
const report = await backend.migrate({ dryRun: true }); // { applied: [], pending: ["0001_consent_records"] }
await backend.migrate();Each statement is safe to run twice, so a migration that stopped halfway, or two instances migrating at once, finish cleanly on the next run.
Another database
A store is an object with seven methods (ConsentStore). The conformance suite proves a new one behaves like the built-in ones.