duckdb-api
June 27, 2026 ยท View on GitHub
Run DuckDB behind a Hono HTTP API in a Node.js Docker container.
This project uses @duckdb/node-api and currently targets DuckDB v1.5.4.
Usage
Install dependencies and run the API locally:
npm install
npm run dev
Build and run the Docker image:
npm run build:docker
npm run dev:docker
The API listens on port 3000 by default.
Docker Extensions
npm run build:docker runs scripts/download_extensions.sh before building the image. The script downloads Linux AMD64 DuckDB v1.5.4 extension binaries for:
httpfsicebergducklakenanoarrow
The app loads httpfs, iceberg, and nanoarrow during initialization.
Environment Variables
PORT: Port to run the API on. Defaults to3000.API_TOKEN: Bearer token forPOST /query.USERNAME: Username for basic auth. RequiresPASSWORD.PASSWORD: Password for basic auth. RequiresUSERNAME.
Basic auth applies to both POST /query and POST /streaming-query when USERNAME and PASSWORD are set.
AWS S3 access:
AWS_REGION: AWS region for S3 access.AWS_ACCESS_KEY_ID: AWS access key ID for S3 access.AWS_SECRET_ACCESS_KEY: AWS secret access key for S3 access.
Cloudflare R2 Data Catalog / Iceberg access:
R2_CATALOG: Catalog name.R2_ENDPOINT: Catalog endpoint URI.R2_TOKEN: Cloudflare API token for R2.
API
Both query endpoints accept a JSON request body with a query property:
{
"query": "SELECT 1 AS value;"
}
User-submitted queries are filtered to block duckdb_settings, INSTALL, LOAD, SET, PRAGMA, and SECRET usage.
POST /query
Executes a SQL query and returns JSON row objects.
Response content type:
application/json
Example:
curl -X POST http://localhost:3000/query \
-H "Content-Type: application/json" \
-d '{"query": "SELECT 1 AS value;"}'
Example response:
[
{
"value": 1
}
]
POST /streaming-query
Executes a SQL query with DuckDB's streaming result API and streams newline-delimited JSON rows.
Response content type:
application/x-ndjson
Example:
curl --location http://localhost:3000/streaming-query \
--header "Content-Type: application/json" \
--data '{
"query": "SELECT * FROM '\''https://shell.duckdb.org/data/tpch/0_01/parquet/orders.parquet'\'' LIMIT 100"
}'
Example response:
{"o_orderkey":1,"o_custkey":36901}
{"o_orderkey":2,"o_custkey":78002}
@duckdb/node-api does not expose the legacy arrowIPCStream() API from the old duckdb package, so this endpoint streams NDJSON instead of Arrow IPC bytes.