Skip to content

Deploying

nola build compiles .tsi modules to JavaScript that runs directly in Node. Deploy the built files with your runtime dependencies and provider credentials.

From the typescript-interop layout, npx nola-lang build --out dist writes:

dist/
nola.config.js # your config, bundled with a self-configuring wrapper
src/
person.tsi.js # the lowered module
person.tsi.js.map # source map back to src/person.tsi
person.tsi.d.ts # types for consumers
  • The output mirrors the source tree: src/person.tsi becomes dist/src/person.tsi.js. Views of cross-file types are emitted as <name>.tsi.js and <name>.tsi.d.ts. Because a view re-exports ./<name>.js, compile the underlying .ts module into the same output tree.
  • For application builds, the default build.target: "app", each compiled module imports the bundled dist/nola.config.js. Importing a built module therefore configures the runtime. The build refuses to overwrite a dist/nola.config.js it did not generate.
  • nola build compiles .tsi files only. A plain .ts entry such as src/main.ts needs a separate compilation step, or you can write a JavaScript entry that imports the built module.

For the typescript-interop template, create a JavaScript entry at the project root:

// entry.mjs — plain JavaScript, next to dist/
import { extractPerson } from "./dist/src/person.tsi.js";
console.log(JSON.stringify(await extractPerson("Alice Smith, 32, is a staff engineer at Acme Corp.")));
Terminal window
npx nola-lang build --out dist
node entry.mjs # no --import, no loader: prints the extraction

Importing ./dist/src/person.tsi.js also loads dist/nola.config.js. Set provider keys such as OPENAI_API_KEY in the process environment; the built application does not load .env automatically.

Relative file paths in the config, such as replay("./nola.replay.jsonl"), resolve against the working directory. Run from the project root or use absolute paths for files the application needs.

For a TypeScript entry, compile it separately with tsc and use the built .tsi.js path in the import. The neighboring .tsi.d.ts supplies its types. Make sure the import resolves from the emitted entry’s location. Direct .tsi imports need Nola’s loader or a bundler plugin; nola run handles them during development.

Set build: { target: "lib" } in nola.config.ts to build a library. Its emitted modules omit the config import, leaving configuration to the consuming application through its loader, bundler plugin or built config.

Publish the .tsi.js and .tsi.d.ts files together. Declare @nola-lang/runtime and any providers the library imports as dependencies, using matching Nola package versions.

For a bundled application, install the plugin for your build tool: @nola-lang/vite, @nola-lang/webpack, @nola-lang/rollup, @nola-lang/rolldown, @nola-lang/esbuild, @nola-lang/rspack or @nola-lang/next. The Next.js integration uses withNola and supports webpack and Turbopack.

The plugins compile .tsi and include the config in the server bundle. Nola inference runs server-side; client imports are rejected with NOLA4001 where the bundler can identify them, with a runtime check as a backstop. See the package READMEs for setup, including @nola-lang/vite and @nola-lang/next.

For deployments using built output:

  • nola-lang stays a devDependency. Deploy @nola-lang/runtime and any provider packages the application imports.
  • The loader is unnecessary because the build has already compiled the Nola syntax. Running source through the loader requires Node; Bun and Deno are rejected with NOLA3015.
  • Package versions should match across Nola dependencies. An incompatible runtime contract or duplicate runtime can prevent startup; see NOLA3001 and NOLA3002.

Next: Nola compared