Skip to content

Bundle an app that uses AskDB

Integrator guide

AskDB bundles into a Node service like most libraries, with one exception: from inside a bundle, @askdb/config can’t load a TypeScript askdb.config.ts. Write the config in JavaScript, keep @askdb/config out of the bundle, or use no config file. This guide covers esbuild and webpack 5.

  • A Node service that calls createAskDb() or ask(). Embed in a Node app sets one up.
  • esbuild, or webpack 5, building for Node.

@askdb/config loads askdb.config.* from disk when your service starts. It loads a JavaScript config (.js, .cjs or .mjs) with Node’s own loader, which works from inside a bundle. A TypeScript config needs jiti, which @askdb/config uses to compile it, and jiti can’t run from inside a bundle. Three setups work:

  • A JavaScript config, createAskDb(): bundle @askdb/config with the rest of your service, and deploy askdb.config.mjs (or .js, .cjs) next to the bundle. Nothing is installed beside the bundle, so the file can’t import anything, @askdb/config included: write a plain export default { … } and read each variable from process.env. This needs @askdb/config 1.0.0-beta.20 or later.
  • A TypeScript config, createAskDb(): leave @askdb/config out of the bundle and install it next to the bundle when you deploy. askdb.config.ts then loads as it does without a bundler.
  • No config, ask(): build the model with the AI SDK in your own code, and load the schema from one JSON file. The bundle then needs nothing installed beside it.

A JavaScript config for a bundled service looks like this. @askdb/config loads .env before the file, so process.env holds what .env sets:

askdb.config.mjs
export default {
ai: {
provider: "openai",
providerConfig: { openai: { apiKey: process.env.OPENAI_API_KEY } },
language: { model: "gpt-4o-mini" },
},
introspection: { provider: "postgres", providerConfig: { postgres: { databaseUrl: process.env.DATABASE_URL } } },
};

Bring your own model helps you choose between them.

Install ai and the AI SDK package of your provider, such as @ai-sdk/openai:

Terminal window
npm install ai @ai-sdk/openai

@askdb/ai loads each provider’s SDK only when it first builds a model. esbuild leaves out the SDKs you don’t install; webpack 5 needs them listed in externals, as step 3 shows. The Vercel AI Gateway needs none. See Provider SDKs.

With a TypeScript config, mark @askdb/config external. With a JavaScript config or none, drop --external:@askdb/config.

Terminal window
# CommonJS output. --external:@askdb/config is for a TypeScript config only.
esbuild src/server.mjs --bundle --platform=node --format=cjs \
--external:@askdb/config --outfile=dist/server.cjs

ESM output also needs a banner that defines require. The AI SDK’s @vercel/oidc calls require("path"), and without the banner the bundle stops at startup with Dynamic require of "path" is not supported:

Terminal window
# ESM output. --external:@askdb/config is for a TypeScript config only.
esbuild src/server.mjs --bundle --platform=node --format=esm \
--external:@askdb/config --outfile=dist/server.mjs \
--banner:js="import { createRequire as topLevelCreateRequire } from 'node:module'; const require = topLevelCreateRequire(import.meta.url);"

esbuild needs nothing for the provider SDKs you didn’t install: the build succeeds, and a provider whose SDK is missing fails only when it first builds a model.

The schema artifact is a directory that your service reads at run time, so the bundler doesn’t include it. Turn it into one JSON file with askdb bundle, and ship that file next to the bundle:

Terminal window
npx askdb bundle ./my-app.schema --out ./my-app.schema.bundle.json

Load it like a schema directory. With a config file, pass it to createAskDb(), or set host.schemaPath to it:

const askdb = createAskDb({
config: getAskDbRuntimeConfig(),
schema: { path: "./my-app.schema.bundle.json" },
});

Without one, load it with loadSchema() and pass your model to ask():

import { ask, loadSchema } from "@askdb/core";
import { openai } from "@ai-sdk/openai";
const schema = loadSchema("./my-app.schema.bundle.json");
const model = openai("gpt-4o-mini"); // reads OPENAI_API_KEY from the environment
const { sql } = await ask({ question, schema, dialect: "postgres", model });

With a JavaScript config, deploy the bundle, askdb.config.mjs, the schema file and your .env or environment variables. Nothing else needs installing. Start the service from that directory, where bootstrapAskDbEnv({ cwd: process.cwd() }) looks for the config.

With a TypeScript config, deploy the bundle, askdb.config.ts, the schema file and your .env or environment variables, and install @askdb/config in that directory:

Terminal window
npm install @askdb/config

It brings jiti and dotenv, which load the config. Start the service from that directory.

Without one, deploy the bundle and the schema file, and set your provider’s key in the environment. Nothing else needs installing.

ErrorFix
createRequire is not a function (webpack); The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received undefined or Cannot find module '../dist/babel.cjs' (esbuild)A TypeScript config, with @askdb/config inside the bundle. Mark it external and install it next to the bundle, or write the config in JavaScript. On @askdb/config older than 1.0.0-beta.20, a JavaScript config fails this way too.
Cannot find module '@askdb/config', or Cannot find package '@askdb/config'@askdb/config is external but not installed where the bundle runs: install it there. Or your JavaScript config imports it: remove the import, and read variables from process.env.
SyntaxError: Unexpected token 'export', or Cannot use import statement outside a moduleNode reads your askdb.config.js as CommonJS. Rename it to askdb.config.mjs, or write it with module.exports = { … }.
Module not found: Error: Can't resolve '@ai-sdk/<name>' (webpack)Install that SDK, or add it to externals.
Provider '<name>' requires the optional peer dependency @ai-sdk/<name>The SDK of the provider your config selects isn’t in the bundle. Install it and bundle again.
Dynamic require of "path" is not supported (esbuild ESM)Add the createRequire banner from step 3.