CLI

Programmatic API

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

Overview

The Better-T-Stack CLI can be used programmatically in your Node.js applications, allowing you to create projects, add features, and manage configurations through JavaScript/TypeScript code instead of shell commands.

Installation

Install the package in your Node.js project:

npm install create-better-t-stack
# or
pnpm add create-better-t-stack
# or
bun add create-better-t-stack

Quick Start

Basic Project Creation

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

async function createProject() {
  const result = await init("my-app", {
    yes: true, // Use defaults, no prompts
    frontend: ["tanstack-router"],
    backend: "hono",
    database: "sqlite",
    orm: "drizzle",
    auth: "better-auth",
    packageManager: "bun",
    install: false, // Don't install deps automatically
    disableAnalytics: true, // Disable analytics
  });

  if (result.success) {
    console.log(`✅ Project created at: ${result.projectDirectory}`);
    console.log(`📝 Reproducible command: ${result.reproducibleCommand}`);
    console.log(`⏱️  Time taken: ${result.elapsedTimeMs}ms`);
  } else {
    console.error(`❌ Failed: ${result.error}`);
  }
}

createProject();

Directory Conflict Handling

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

const result = await init("existing-folder", {
  yes: true,
  directoryConflict: "increment", // Creates "existing-folder-1"
  renderTitle: false, // Hide ASCII art
});

Disabling Analytics

You can disable analytics:

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

const result = await init("my-private-project", {
  yes: true,
  disableAnalytics: true, // No analytics data will be sent
  frontend: ["tanstack-router"],
  backend: "hono",
});

Note: Analytics help improve Better-T-Stack by providing insights into usage patterns. When disabled, no data is collected or transmitted.

API Reference

init(projectName?, options?)

Creates a new Better-T-Stack project.

Parameters:

  • projectName (string, optional): Project name or directory path
  • options (CreateInput, optional): Configuration options

Returns: Promise<InitResult>

Example:

const result = await init("my-project", {
  frontend: ["next"],
  backend: "hono",
  database: "postgres",
  orm: "drizzle"
});

sponsors()

Display Better-T-Stack sponsors (same as CLI).

Returns: Promise<void>

docs()

Open documentation in browser (same as CLI).

Returns: Promise<void>

builder()

Open web-based stack builder (same as CLI).

Returns: Promise<void>

Type Definitions

CreateInput

Configuration options for project creation:

interface CreateInput {
  projectName?: string;
  yes?: boolean;                    // Skip prompts, use defaults
  yolo?: boolean;                   // Bypass validations (not recommended)
  verbose?: boolean;               // Show JSON result (CLI only, programmatic always returns result)
  database?: Database;              // "none" | "sqlite" | "postgres" | "mysql" | "mongodb"
  orm?: ORM;                        // "none" | "drizzle" | "prisma" | "mongoose"
  auth?: boolean;                   // Include authentication
  frontend?: Frontend[];            // Array of frontend frameworks
  addons?: Addons[];               // Array of addons
  examples?: Examples[];           // Array of examples
  git?: boolean;                   // Initialize git repo
  packageManager?: PackageManager; // "npm" | "pnpm" | "bun"
  install?: boolean;               // Install dependencies
  dbSetup?: DatabaseSetup;         // Database hosting setup
  backend?: Backend;               // Backend framework
  runtime?: Runtime;               // Runtime environment
  api?: API;                       // API type
  webDeploy?: WebDeploy;          // Web deployment setup
  serverDeploy?: ServerDeploy;    // Server deployment setup
  directoryConflict?: DirectoryConflict; // "merge" | "overwrite" | "increment" | "error"
  renderTitle?: boolean;           // Show ASCII art title
  disableAnalytics?: boolean;      // Disable analytics and telemetry
}

InitResult

Result object returned by init():

interface InitResult {
  success: boolean;                // Whether operation succeeded
  projectConfig: ProjectConfig;    // Final project configuration
  reproducibleCommand: string;     // CLI command to recreate project
  timeScaffolded: string;          // ISO timestamp of creation
  elapsedTimeMs: number;          // Time taken in milliseconds
  projectDirectory: string;        // Absolute path to project
  relativePath: string;           // Relative path to project
  error?: string;                 // Error message if failed
}

Configuration Options

Directory Conflict Resolution

Control how existing directories are handled:

// Merge with existing files (careful with conflicts)
directoryConflict: "merge"

// Completely overwrite existing directory
directoryConflict: "overwrite"

// Create new directory with incremented name (my-app-1)
directoryConflict: "increment"

// Throw error if directory exists
directoryConflict: "error"

Title Rendering

Control CLI output appearance:

// Hide ASCII art title (useful for automated scripts)
renderTitle: false

// Show ASCII art title (default)
renderTitle: true

YOLO Mode

Bypass validation checks (not recommended):

// Skip compatibility validations
yolo: true

Error Handling

The programmatic API uses different error handling than the CLI:

Directory Conflicts

Directory conflict errors return structured results instead of throwing:

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

if (!result.success) {
  console.log(result.error); // "Directory exists and is not empty..."
  // Handle gracefully instead of process exit
}

Validation Errors

Validation errors still throw exceptions (maintains CLI compatibility):

try {
  await init("test", {
    database: "mongodb",
    orm: "drizzle" // Invalid combination
  });
} catch (error) {
  console.error(error.message); // "MongoDB requires Mongoose or Prisma ORM"
}

Migration from CLI

CLI Command to Programmatic API

Convert CLI commands to programmatic calls:

# CLI Command
create-better-t-stack my-app \
  --frontend tanstack-router \
  --backend hono \
  --database postgres \
  --orm drizzle \
  --auth better-auth \
  --yes
// Programmatic Equivalent
const result = await init("my-app", {
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "postgres",
  orm: "drizzle",
  auth: "better-auth",
  yes: true
});

Handling Prompts

CLI prompts become explicit options:

// Instead of interactive prompts, specify all options
const result = await init("my-app", {
  yes: true, // Skip all prompts
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "postgres",
  orm: "drizzle",
  auth: "better-auth",
  addons: ["biome", "turborepo"],
  examples: ["todo"],
  packageManager: "bun",
  install: false,
  git: true
});