Deploy AskDB as an HTTP service
Service guide
@askdb/http-api wraps the same ask() pipeline behind a minimal HTTP server. Use it when non-Node clients need to ask questions, or when you want one AskDB service shared across multiple consumers.
Install
Section titled “Install”npm install @askdb/http-api ai @ai-sdk/openaipnpm add @askdb/http-api ai @ai-sdk/openaiyarn add @askdb/http-api ai @ai-sdk/openaiStart the server
Section titled “Start the server”The package ships an askdb-http binary — for most deployments you don’t write any server code:
npx askdb-http --schema-path ./my-app.schema --port 3000All flags are optional. Each falls back to askdb.config.ts: the schema to host.schemaJson, then host.schemaPath, and the port and host to httpApi.listen.port and httpApi.listen.host. After that, the port defaults to 3000 and the host to 127.0.0.1. The schema has no default: with none configured, POST /ask answers 500 generation_not_configured with a generic message, and the server log names the cause (errName SchemaNotConfiguredError).
To embed the server in your own process instead, use the programmatic API:
import { createAskDbHttpServer } from "@askdb/http-api";
const server = createAskDbHttpServer({ port: 3000, host: "127.0.0.1", maxBodyBytes: 1024 * 1024, // 1 MiB default});
await server.listen();Either way, the server reads its schema and model settings from your askdb.config.ts, the same config file the CLI and Studio use. It loads the config from the current working directory. createAskDbHttpServer() reads that directory’s .env, and askdb-http the first of several .env files (HTTP API reference lists them). If your process already called bootstrapAskDbEnv(), the server uses that config instead. Set host.schemaPath to point at your schema artifact and configure the AI provider as usual — supply the provider key (OPENAI_API_KEY, etc.) in your environment the same way you do for the CLI.
Two kinds of schema don’t work with this server. A schema with a tenant policy can’t be served yet: see Schemas with a tenant policy. A schema whose provider has no AskDB dialect answers 500 generation_not_configured until you set dialect in askdb.config.ts; the server doesn’t fall back to PostgreSQL. The dialect you set applies to the server’s own schema only: a request schemaJson gets SQL for its own provider, and 400 bad_request when it records none or one AskDB has no dialect for.
On a platform that sets PORT
Section titled “On a platform that sets PORT”Some platforms give a service its port in a PORT variable. Read it in askdb.config.ts and convert it to a number: env() returns a string, so port: env("PORT") doesn’t type-check.
import { defineConfig, env } from "@askdb/config";
export default defineConfig({ // ... httpApi: { listen: { port: Number(env("PORT") ?? 3000) }, },});With PORT unset, Number(env("PORT")) is NaN, which counts as unset, so the server uses 3000 either way. The ?? 3000 shows the fallback in the config.
Ask a question
Section titled “Ask a question”The server has two endpoints. GET /health answers {"ok":true}, for a liveness probe. POST /ask takes a JSON body with a question, and returns the checked SQL in sql, with a correlationId and the model’s token usage. It returns SQL only: the server doesn’t run it, and your client decides what to do with it. The HTTP API reference lists every request field, response field and error code.
From curl
Section titled “From curl”curl -X POST http://localhost:3000/ask \ -H "content-type: application/json" \ -d '{"question": "How many customers signed up last week?"}'From Python
Section titled “From Python”import osimport httpx
resp = httpx.post( f"{os.environ['SERVER_URL']}/ask", json={"question": "How many customers signed up last week?"}, timeout=30,)if not resp.headers.get("content-type", "").startswith("application/json"): resp.raise_for_status() # a gateway's own error page, not AskDB'sbody = resp.json() # AskDB's own error responses are JSON too, with ok: false
if body["ok"]: sql = body["sql"] correlation_id = body["correlationId"] # Run the SQL against your read-only role here.else: raise RuntimeError(f'{resp.status_code} {body["error"]["code"]}: {body["error"]["message"]}')Run it behind a gateway
Section titled “Run it behind a gateway”@askdb/http-api is intentionally minimal — there is no built-in auth, rate limiting, or TLS termination. Don’t expose it directly to the public internet. Put it behind your existing API gateway, ingress, or reverse proxy and apply the same auth you use for the rest of your services.
A typical production shape:
@askdb/http-api for SQL, and runs it under a read-only role. The server never connects to your database.Execution is your gateway service’s job, because that’s where your auth context lives.
Schemas with a tenant policy
Section titled “Schemas with a tenant policy”@askdb/http-api can’t serve a schema that has a tenant policy (tenant-policy.md) yet. A request has no way to carry a tenant scope, and AskDB never makes one up, so it refuses every such request before calling the model. #343 plans a server-side scope hook, and #277 a scope for the askdb-http binary. Until then, such a schema answers every request with 500 tenant_scope_not_configured. Serve it from your own Node service with ask({ tenantScope }) instead: see Multi-tenancy.
Custom schema per request
Section titled “Custom schema per request”The default is to load the schema from host.schemaPath once at boot. Per-request schemas are off by default: a request that sends schemaJson gets 403 schema_override_disabled. An override is a schema artifact the server parses and puts into the prompt through your model key, so a caller who can send schemaJson controls what AskDB loads. Only enable it for tests or callers you trust, and run @askdb/http-api 1.0.0-beta.48 or later, whose @askdb/core fixes GHSA-qqx9-qpw2-wcqg. @askdb/http-api pins its @askdb/core, so upgrade @askdb/http-api itself; npm ls @askdb/core should show only 1.0.0-beta.48 or later. For a multi-product deployment where different consumers send different schemas, turn it on in askdb.config.ts:
httpApi: { allowSchemaOverride: true },Then supply the bundled JSON as schemaJson in the request body:
{ "question": "How many users?", "schemaJson": "{\"version\":2,\"schemaId\":\"…\",\"tables\":[…]}"}Bundle the artifact with askdb bundle and ship the resulting JSON as the value.
Correlation IDs
Section titled “Correlation IDs”Every response except GET /health includes a correlationId: the value of the request’s x-correlation-id header when the caller sends one, or a new UUID. The server tags its structured log events with the same ID. Send x-correlation-id from your gateway to join its logs to the generated SQL and any errors.
Read next
Section titled “Read next”© 2026 Yahya Gilany

