Skip to content
Lakefront
Esc
↑↓navigate↵open⌘Jpreview
On this page

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 lakefront
pnpm add lakefront
yarn add lakefront
bun add lakefront

2. 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.

Last updated on September 10, 2026

Was this page helpful?