FastAPI-class HTTP framework inside DuckDB — CREATE ROUTE turns SQL into typed, validated endpoints; one process is DB + HTTP (+ PDF/renderer via companion extensions).
Installing and Loading
INSTALL quackapi FROM community;
LOAD quackapi;
Example
LOAD quackapi;
-- JSON endpoint: the SELECT is the handler
CREATE ROUTE hello GET '/hello' AS SELECT 'world' AS msg;
-- Path params bind as $id; cast failure → FastAPI-shaped 422
CREATE ROUTE item GET '/items/:id' AS SELECT $id::INTEGER AS id;
-- HTML when the single output column is named html
CREATE ROUTE home GET '/' AS SELECT '<h1>quackapi</h1>' AS html;
-- Typed optional query param with constraint
CREATE ROUTE search GET '/search'
PARAM limit INTEGER DEFAULT 10 LE 100
AS
SELECT $q::VARCHAR AS q, $limit::INTEGER AS limit;
-- POST mutation (JSON body fields bind as $name / $age)
CREATE ROUTE create_user POST '/users' STATUS 201 AS
SELECT $name::VARCHAR AS name, $age::INTEGER AS age;
-- Serve (background threads; shell stays usable)
-- Optional: static_dir for unrouted GETs; cors_origins for browser CORS
SELECT * FROM quackapi_serve(8000, static_dir := './static');
-- curl http://127.0.0.1:8000/hello → [{"msg":"world"}]
-- curl http://127.0.0.1:8000/items/42 → [{"id":42}]
-- curl http://127.0.0.1:8000/items/abc → 422 detail[loc=path,id]
-- curl -X POST …/users -H 'Content-Type: application/json' -d '{"name":"a","age":3}'
SELECT * FROM quackapi_routes();
SELECT * FROM quackapi_stop(8000);
About quackapi
quackapi
A FastAPI-class HTTP framework that lives inside DuckDB. Routes are DDL, handlers are SQL, and request validation is the database type system.
Thesis: the backend IS the database
A conventional stack serializes at every hop (app server → workers → DB → PDF microservice). quackapi collapses that into one DuckDB process:
browser → DuckDB [ quackapi(HTTP) · pdf · tera · fakeit · webbed · … ]
Companion community extensions (pdf, tera, fakeit, crawler/webbed,
cronjob, curl_httpfs, …) load into the same address space. A handler that
redacts a PDF is literally SELECT pdf_redact(...) — no RPC between tiers.
Showcase: the Closure redaction-review app (DB + HTML UI + PDF word
boxes + POST decisions + static assets) runs as a single DuckDB process via
CREATE ROUTE + quackapi_serve.
CREATE ROUTE
CREATE [OR REPLACE] ROUTE <name> <METHOD> '<pattern>'
[STATUS <n>] [REQUIRE <auth>] [GROUP <g>]
[BODY SCHEMA '<json-schema>']
[PARAM <name> [<type>] [HEADER|COOKIE|QUERY …] [DEFAULT …] [GE|LE|…] …]
AS <select>;
- Methods: GET, POST, PUT, DELETE, PATCH, HEAD.
- Path segments
:id/{id}and query/body fields bind to$id. - Cast/constraint failures return 422 with FastAPI-shaped
{"detail":[{"loc":[...],"msg":...,"type":...}]}. - Default JSON response is an array of row objects (SQL result-set
semantics). A single column named
htmlortextreturns raw HTML/text;location/set_cookieset response headers. quackapi_serve([port], host := …, static_dir := …, cors_origins := …, memory_limit := …)uses DuckDB's bundled httplib (background listener + workers).- Built-in OpenAPI:
GET /openapi.json,GET /docs,GET /redoc.
Other DDL / functions
- Auth:
CREATE AUTH … AS API_KEY | JWT (SECRET … [, ALGORITHM HS256]),REQUIREon routes,quackapi_add_api_key,quackapi_auths, claims as$claims_*. - Groups:
CREATE GROUP … WITH (prefix=…, auth=…, tags=…)— path prefix- auth inheritance (
quackapi_groups()).
- auth inheritance (
- Table API:
CREATE API FOR TABLE t [AT '/path'] [KEY 'id']→ GET list- GET by key only.
- Queue:
CREATE QUEUE,quackapi_enqueue/dequeue/ack/nack, tablequackapi_jobs(broker-less; composecronjobfor workers). - SSE:
CREATE STREAM … GET '/path' AS <select>(text/event-stream; WebSocket not supported on httplib). - Policies:
CREATE ROW ACCESS POLICY/CREATE MASKING POLICY+ALTER TABLEbind;quackapi_policies(). - Inspect:
quackapi_routes(),quackapi_servers(),quackapi_http_util_name().
Platforms & build
Targets DuckDB v1.5.4. C++17; depends only on DuckDB-bundled httplib and mbedtls (no vcpkg, no libcurl). CI excludes wasm and all Windows legs until a green windows_amd64 community build exists. Signed community binaries land after this descriptor is accepted.
Limits (honest)
- DuckDB single-writer / file concurrency — not high-write multi-tenant OLTP.
- Serve
memory_limitdefault is 256MB only when nothing is configured; usememory_limit := '4GB'/SET quackapi_memory_limit(never clobbers an operatorSET memory_limit). - Request body max 8 MiB; JWT HS256 only; table API is read-only scaffold.
- Route registry is instance-scoped (re-run DDL after reopen); queue jobs
persist in
quackapi_jobs.
Full reference: https://github.com/asubbarao/quackapi
Community page draft: https://github.com/asubbarao/quackapi/blob/main/docs/community-page.md
Added Functions
| function_name | function_type | description | comment | examples |
|---|---|---|---|---|
| quack_from_express | table | NULL | NULL | |
| quack_from_express_models | table | NULL | NULL | |
| quack_from_fastapi | table | NULL | NULL | |
| quack_from_fastapi_models | table | NULL | NULL | |
| quack_from_gin | table | NULL | NULL | |
| quack_from_gin_models | table | NULL | NULL | |
| quack_from_rails | table | NULL | NULL | |
| quack_from_rails_models | table | NULL | NULL | |
| quack_from_x_sql | scalar | NULL | NULL | |
| quack_from_x_sql_relpath | scalar | NULL | NULL | |
| quackapi_ack | scalar | NULL | NULL | |
| quackapi_add_api_key | table | NULL | NULL | |
| quackapi_authentication | scalar | NULL | NULL | |
| quackapi_authorization | scalar | NULL | NULL | |
| quackapi_auths | table | NULL | NULL | |
| quackapi_dequeue | table | NULL | NULL | |
| quackapi_enqueue | scalar | NULL | NULL | |
| quackapi_groups | table | NULL | NULL | |
| quackapi_http_util_name | scalar | NULL | NULL | |
| quackapi_nack | scalar | NULL | NULL | |
| quackapi_policies | table | NULL | NULL | |
| quackapi_queues | table | NULL | NULL | |
| quackapi_routes | table | NULL | NULL | |
| quackapi_serve | table | NULL | NULL | |
| quackapi_servers | table | NULL | NULL | |
| quackapi_stop | table | NULL | NULL | |
| quackapi_streams | table | NULL | NULL | |
| quackapi_verify_auth | scalar | NULL | NULL |
Overloaded Functions
This extension does not add any function overloads.
Added Types
This extension does not add any types.
Added Settings
| name | description | input_type | scope | aliases |
|---|---|---|---|---|
| quackapi_compression | Enable Accept-Encoding response compression on quackapi_serve (zstd preferred, then gzip). Default true. Overridden by compression named parameter. | BOOLEAN | GLOBAL | [] |
| quackapi_compression_min_bytes | Minimum response body size (bytes) before compression. Default 256. Overridden by compression_min_bytes named parameter. | BIGINT | GLOBAL | [] |
| quackapi_cors_origins | CORS allowed origins for quackapi_serve (* or comma-separated list). Empty (default) disables CORS. Overridden by cors_origins named parameter. | VARCHAR | GLOBAL | [] |
| quackapi_http_client | Outbound HTTP client for httpfs/route fetches: auto|curl|httplib. Default auto prefers curl_httpfs (connection pool, HTTP/2, async IO) and falls back to httplib when unavailable. Overridden by http_client named parameter. Does not change the inbound HTTP server. | VARCHAR | GLOBAL | [] |
| quackapi_log_level | Log verbosity for quackapi_serve: silent|error|warn|info|debug. Default info. Overridden by log_level named parameter. | VARCHAR | GLOBAL | [] |
| quackapi_memory_limit | Memory limit applied by quackapi_serve (e.g. '4GB', '512MB'). Empty (default): do not clobber a non-default DuckDB memory_limit; only apply the 256MB serve default when nothing was configured. Overridden by memory_limit named parameter. | VARCHAR | GLOBAL | [] |