CLI

Programmatic API

Use Better-T-Stack programmatically in your Node.js applications

Overview

You can call Better-T-Stack directly from TypeScript/JavaScript without shelling out to the CLI.

The programmatic API is exported from create-better-t-stack and is designed for automation tools, internal generators, and scripted workflows.

Because it runs in silent mode by default, it also benefits from the same agent-safe behavior as create-json, including structured addon and database setup options.

Installation

npm i create-better-t-stack

Quick Start

import { create } from "create-better-t-stack";

const result = await create("my-app", {
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "sqlite",
  orm: "drizzle",
  auth: "better-auth",
  packageManager: "bun",
  install: false,
});

result.match({
  ok: (data) => {
    console.log(`Project created at: ${data.projectDirectory}`);
    console.log(`Reproducible command: ${data.reproducibleCommand}`);
  },
  err: (error) => {
    console.error(`Failed: ${error.message}`);
  },
});

API Reference

create(projectName?, options?)

Create a new project.

function create(
  projectName?: string,
  options?: Partial<CreateInput>,
): Promise<Result<InitResult, CreateError>>;

Notes:

  • Uses the same option model as the CLI create command (frontend, backend, database, orm, api, auth, addons, etc.).
  • Supports structured addonOptions and dbSetupOptions.
  • Supports dryRun for validation-only automation.
  • Validates JavaScript inputs at runtime using the same schemas as the CLI.
  • Runs in silent mode (no interactive prompts / no CLI UI output).
  • Returns a Result (ok/err) instead of exiting the process.

add(options?)

Add addons to an existing Better-T-Stack project.

function add(options?: {
  addons?: Addons[];
  addonOptions?: AddonOptions;
  install?: boolean;
  packageManager?: PackageManager;
  projectDir?: string;
  dryRun?: boolean;
}): Promise<AddResult>;

Example:

import { add } from "create-better-t-stack";

const result = await add({
  projectDir: "./my-app",
  addons: ["biome", "mcp"],
  addonOptions: {
    mcp: {
      scope: "project",
      servers: ["context7"],
      agents: ["cursor"],
    },
  },
  install: true,
});

if (result.success) {
  console.log(`Added: ${result.addedAddons.join(", ")}`);
} else {
  console.error(result.error ?? "Failed to add addons");
}

add() validates its input at runtime and always returns an AddResult. Invalid input and addon failures return { success: false, error } instead of throwing or exiting the process.

createVirtual(options)

Generate a project in memory without writing to disk.

import { createVirtual } from "create-better-t-stack";

const result = await createVirtual({
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "sqlite",
  orm: "drizzle",
  addonOptions: {
    wxt: {
      template: "react",
    },
  },
});

if (result.isErr()) {
  console.error(result.error.message);
}

This is useful for previews, tests, and web-based builders. Invalid input or incompatible project configurations are returned as a GeneratorError with the validation phase.

sponsors()

Show sponsors (same behavior as CLI command).

docs()

Open docs URL (same behavior as CLI command).

builder()

Open the web stack builder (same behavior as CLI command).

Result Types

InitResult (from create on ok)

type InitResult = {
  success: boolean;
  projectConfig: ProjectConfig;
  reproducibleCommand: string;
  timeScaffolded: string;
  elapsedTimeMs: number;
  projectDirectory: string;
  relativePath: string;
  error?: string;
};

AddResult (from add)

type AddResult = {
  success: boolean;
  addedAddons: Addons[];
  projectDir: string;
  dryRun?: boolean;
  plannedFileCount?: number;
  error?: string;
};

CreateError

create() can return these error types in Result.err(...):

  • UserCancelledError
  • CLIError
  • ProjectCreationError

The CreateInput, AddOptions, ProjectConfig, InitResult, and AddResult types are exported from create-better-t-stack.

Error Handling Pattern

import { create } from "create-better-t-stack";

const result = await create("existing-dir", {
  directoryConflict: "error",
});

if (result.isErr()) {
  console.error(result.error.message);
  process.exit(1);
}

console.log(result.value.projectDirectory);

Mapping CLI to Programmatic

CLI:

create-better-t-stack my-app \
  --frontend tanstack-router \
  --backend hono \
  --database postgres \
  --orm drizzle \
  --auth better-auth

Programmatic:

const result = await create("my-app", {
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "postgres",
  orm: "drizzle",
  auth: "better-auth",
  addonOptions: {
    wxt: { template: "react" },
  },
  dbSetupOptions: {
    mode: "manual",
  },
});

For the CLI-side JSON equivalents, see Agent Workflows.

On this page