Skip to content

Embed AskDB in a Node app

Integrator guide

Drop @askdb/core into a TypeScript service. AskDB loads your schema artifact, generates validated SQL from a question, and hands it back. Your service runs it through its own connection pool against a read-only role.

Terminal window
npm install @askdb/core @ai-sdk/openai pg
PackageWhy
@askdb/coreThe pipeline — loadSchema, ask, validation.
Dialects for all four engines are built into @askdb/core; install @askdb/postgres only if this service also runs askdb introspect programmatically.
@ai-sdk/openaiOpenAI’s AI SDK provider — bring your own model provider.
pgThe Postgres driver. AskDB doesn’t open the connection; your app does.

The schema artifact is read from disk. Load it at startup and keep it in memory — it doesn’t need to be reloaded per request.

schema.ts
import { loadSchema } from "@askdb/core";
// Point at the artifact directory that `askdb introspect` wrote —
// your `introspection.outputDir` (e.g. ./my-app.schema or the default ./askdb).
// loadSchema also accepts a bundled .bundle.json file.
export const schema = await loadSchema("./my-app.schema");

./my-app.schema is a directory, not a file — <name>.schema/ is AskDB’s convention for the introspected-and-enriched artifact. loadSchema autodetects a directory, a bundled JSON, or a bare schema.json.

ask-handler.ts
import { ask } from "@askdb/core";
import { openai } from "@ai-sdk/openai";
import { Pool } from "pg";
import { schema } from "./schema.js";
const model = openai("gpt-4o-mini");
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
// Run under a read-only role for defense in depth.
statement_timeout: 5000,
max: 10,
});
export async function askQuestion(question: string) {
const { sql } = await ask({
question,
schema,
dialect: "postgres",
model,
});
// Audit hook — log every question and generated SQL.
console.log({ question, sql });
const { rows } = await pool.query(sql);
return { sql, rows };
}

That’s the whole pipeline. AskDB returns validated SQL — your handler logs it, runs it, and returns the result.

Shorter: let config resolve schema + model

Section titled “Shorter: let config resolve schema + model”

If you already use askdb.config.ts, @askdb/client removes the manual schema load and model construction. createAskDb resolves both from config; you pass only the question.

Terminal window
npm install @askdb/client @askdb/ai @askdb/ai-openai @askdb/config pg
ask-handler.ts
import { createAskDb } from "@askdb/client";
import { getAskDbRuntimeConfig } from "@askdb/config";
import { createAiRegistry } from "@askdb/ai";
import { openaiProvider } from "@askdb/ai-openai";
import { Pool } from "pg";
const askdb = createAskDb({
config: getAskDbRuntimeConfig(), // after bootstrapAskDbEnv() at startup
registry: createAiRegistry([openaiProvider]),
schema: { path: "./my-app.schema" }, // or set host.schemaPath in config and omit
});
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export async function askQuestion(question: string) {
const { sql } = await askdb.ask(question);
const { rows } = await pool.query(sql);
return { sql, rows };
}

Same ask() call, different dialect string. Change dialect: "postgres" to "mysql", "sqlite", or "sqlserver". See Switch engines for the full migration matrix.