Quickstart
Install Lakefront, start the catalog Postgres, and write and read your first board.
Lakefront requires Bun 1.2 or later. The package ships TypeScript source, and Node cannot run it.
1. Install
npm install lakefrontpnpm add lakefrontyarn add lakefrontbun add lakefront2. Start the catalog Postgres
The catalog is where writes commit: the WAL table lives here, and so does the DuckLake truth store’s metadata. A plain Postgres 17 is all the quickstart needs. Parquet files go to a local directory, so there is no object store yet; a multi-node deployment adds S3, covered in the cluster guide.
services:
pg:
image: postgres:17
environment:
POSTGRES_USER: lakefront
POSTGRES_PASSWORD: lakefront
POSTGRES_DB: lakefront
ports: ["5432:5432"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lakefront -d lakefront"]
interval: 2s
timeout: 3s
retries: 30
docker compose up -d
3. Write and read a board
There is no CREATE TABLE. A board is created on its first write, and its columns are inferred from the data. The schema guide covers how inference works and how to declare a schema instead.
import { open } from "lakefront";
await using engine = await open({
postgres: "postgres://lakefront:lakefront@127.0.0.1:5432/lakefront",
data: "./data/lake/",
topology: "single-node",
});
await engine.insertMany("tasks", [
{ id: "task-1", title: "Ship the thing", status: "open" },
{ id: "task-2", title: "Fix the bug", status: "open" },
]);
const read = (
await engine.query("tasks", "SELECT id, title, status FROM {{table}} ORDER BY id")
).unwrap("read");
console.log(read.result.rows);
bun run quickstart.ts
Two required fields, postgres and data, and defaults for everything else. Engine is AsyncDisposable, so await using closes the connection pool, the WAL, and the background flusher when the script’s scope ends.
4. Read your own write
Every write returns the watermark it committed at. Hand it back as minWatermark, and the read waits for a replica that has caught up to it rather than risking a stale one:
const watermark = (await engine.update("tasks", "task-2", { status: "done" })).unwrap("update");
const fresh = (
await engine.query("tasks", "SELECT status FROM {{table}} WHERE id = $1", {
params: ["task-2"],
minWatermark: watermark,
})
).unwrap("read");
The watermark model behind this is on the consistency page.
Where next
- The tutorial walks the same board through schema evolution, a declared strict board, a cross-board transaction, watch, and HTTP serving.
- Concepts covers the design behind what just ran: durability, write paths, routing.