duckweb

A DuckDB extension for serving HTTP responses straight out of SQL.

An app supplies one table macro. Its single VARCHAR column, concatenated, is a complete HTTP/1.1 response — status line, headers, chunked body, terminator. Nothing else is required, and duckweb defines no tables of its own.

LOAD duckweb;

CREATE MACRO render(method, route, headers, body) AS TABLE (
            SELECT duckweb_head(200, 'text/html', '') AS chunk
            FROM duckweb_match(route, '/hello/:name')
  UNION ALL SELECT duckweb_chunk('<h1>hello ' || duckweb_escape(m.name) || '</h1>')
            FROM duckweb_match(route, '/hello/:name') m
  UNION ALL SELECT duckweb_end()
            FROM duckweb_match(route, '/hello/:name')
);

SELECT duckweb_serve('127.0.0.1', 8080, '.', 'memory.main');

That is a running site. The page you are reading is one too — these docs are a duckweb app, served by the DuckDB process that also hands out the extension below.

Install

INSTALL httpfs; LOAD httpfs;
INSTALL duckweb FROM 'https://duckweb.jamesst.one';
LOAD duckweb;

Any DuckDB offering C extension API v1.5.6, started with -unsigned. The builds served here are made and tested against v2.0.0. The extension is not signed by DuckDB’s own key — that key is compiled into the binary, so no third party can sign for it. httpfs is what fetches from a custom repository; DuckDB installs it from its own repository the first time.

Where to go

The tutorial builds birdlog from an empty file to a running server: a filtered listing, a detail page whose chart is drawn by SQL and streamed into a slot, and a stylesheet — all out of one .sql file. It is also where the rules an app has to follow are explained rather than listed.

Rules an app has to follow is the short version of those, and the page to reread when a route starts emitting into somebody else’s response.

The reference covers every function the extension registers. Its signatures are read out of the extension at build time, so they cannot drift from what you have loaded.

Serving covers sockets, files, HTTPS and running several sites on one port. The browser tiers cover the same macro rendering in a SharedWorker and a service worker.

Charts is a second extension, svg_charts: it returns a complete <svg> as a column, so a chart is just part of a query result — no JavaScript, no web fonts, no requests at render time. Every chart on that page is the output of the SQL printed above it.

See it locally

nix run github:jamesmstone/duckweb#demo

serves the starter app on http://127.0.0.1:8080, and

nix flake init -t github:jamesmstone/duckweb

leaves a repo of three files: the SQL, a flake that builds it into a database and serves it, and a README.

A warning worth reading twice

duckweb_serve binds loopback by default and has no authentication. Binding any other interface is an explicit choice, and there is nothing in front of it. Your render macro is the entire request path.

Status

Pre-1.0 and unstable. The API will move while the seam has only been validated against the apps in this repository.