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

Server

serve, the HTTP front end that puts a server node's routes, auth, and SSE stream in front of an Engine.

import { serve } from "lakefront/server";

Opens an Engine and puts an HTTP front end on it: routes, verb-gated auth, and an SSE change stream.

Functions

serve

function serve(options: ServeOptions): Promise<Server>;
Parameter Type Default Description
options ServeOptions (required) See ServeOptions.

Returns the running Server.

const node = await serve({
  postgres: "postgres://lakefront:lakefront@127.0.0.1:5432/lakefront",
  data: "./data",
  port: 8080,
});

Opens an Engine (via open, from lakefront) and puts an HTTP front end on it. ServeOptions extends OpenOptions; see the engine reference for every inherited knob, including the two required fields (postgres, data) and everything open() defaults for you.

Endpoints

Route Method What it does
/health GET Liveness. Does no DuckDB work. A cold node still answers.
/rpc/:procedure POST Every procedure in the wire contract (query.execute, mutate.*, changes.read, node.stats).
/watch GET Server-sent-events change stream.
anything else any 404, { "error": "not found" }.

See the wire protocol reference for request and response formats, headers, status codes, and SSE events.

Verb gate

Every procedure requires a verb, checked against the token’s claims (with auth on) before its handler runs:

Procedure Verb
query.execute read
changes.read read
node.stats read
mutate.insert write
mutate.update write
mutate.delete write

/watch is gated by the same read verb outside this table, since it is not a /rpc/:procedure call.

schema and admin are valid verbs on a token (see the wire contract) but no wire procedure currently requires either: schema and lifecycle operations (createBoard, dropBoard, addColumn, and so on) are engine-level admin decisions, not exposed over HTTP today, so those two verbs gate nothing on the wire.

Types

ServeOptions

OpenOptions plus three fields:

Field Type Default Notes
port number 0 0 asks the OS to pick a free port; read back the bound port from the returned node.
nodeId string `node-${process.pid}` Identifies this node in node.stats() and a query’s servedBy field. Set it explicitly in anything with more than one process.
auth AuthConfig unset Capability-token auth. Unset means auth is off: every request is permitted, the zero-config local default the tour and demo run under.

AuthConfig

type AuthConfig = { secret: string | Uint8Array } | { registry: true };
Variant Behavior
{ secret } Verifies every token with one static HMAC key (kid "env").
{ registry: true } Loads rotating keys from the catalog Postgres, keyed by kid.

Minting, attenuating, and rotating tokens are covered in the auth guide.

Server

interface Server {
  readonly nodeId: string;
  readonly port: number;
  readonly url: string;
  readonly engine: Engine;
  stop(): Promise<void>;
}

engine is the underlying Engine instance directly: flush, maintain, stats, evict, watch, and everything else in the operations guide is reachable without a second connection to the lake. stop() closes the HTTP server, waits for in-flight replica work to settle, closes the auth key registry if one was opened, and closes the engine.

NodeContext

interface NodeContext {
  readonly boardId: string;
  readonly engine: Engine;
  readonly nodeId: string;
  readonly actor?: string; // verified audit identity, when auth is on
}

Import handlers and NodeContext from lakefront/server to use the procedure implementations in your own HTTP server.

boardId and actor are expected to come from your own request handling (board resolved from a verified claim or header, never from the SQL): handlers itself does no auth and no board resolution.

Constants

handlers

const handlers: Handlers<NodeContext>;

The implementation of every procedure in the contract, keyed by procedure name. What serve’s /rpc/:procedure route dispatches into.

Last updated on September 10, 2026

Was this page helpful?