# Programmatic API (/docs/cli/programmatic-api)



## Overview [#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 [#installation]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm i create-better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add create-better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add create-better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add create-better-t-stack
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Quick Start [#quick-start]

```typescript
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 [#api-reference]

### `create(projectName?, options?)` [#createprojectname-options]

Create a new project.

```typescript
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?)` [#addoptions]

Add addons or scaffold a workspace package in an existing Better-T-Stack project.

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

Example:

```typescript
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 or package scaffolding failures return `{ success: false, error }` instead of throwing or exiting the process.

### `createVirtual(options)` [#createvirtualoptions]

Generate a project in memory without writing to disk.

```typescript
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()` [#sponsors]

Show sponsors (same behavior as CLI command).

### `docs()` [#docs]

Open docs URL (same behavior as CLI command).

### `builder()` [#builder]

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

## Result Types [#result-types]

### `InitResult` (from `create` on `ok`) [#initresult-from-create-on-ok]

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

### `AddResult` (from `add`) [#addresult-from-add]

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

### `CreateError` [#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 [#error-handling-pattern]

```typescript
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 [#mapping-cli-to-programmatic]

CLI:

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

Programmatic:

```typescript
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](/docs/cli/agent-workflows).
