Better-TS-Stack

Usage

The better-ts-stack prompt flow and command-line flags

How the CLI Works

Run the command, answer the prompts, and the CLI generates a project from your answers. Every prompt also has a flag, so scripts and CI can run it without prompts.

Run npx better-ts-stack and follow the prompts.

Flags

better-ts-stack [project-path] [options]

Options you leave out are asked interactively, unless you pass --yes.

# No prompts at all
npx better-ts-stack my-api --framework nestjs --database prisma --auth --yes

# Create the project in the current (empty) directory
npx better-ts-stack . --framework nextjs --database drizzle --pm pnpm

# List what would be created without writing anything
npx better-ts-stack my-api --framework express --yes --dry-run
FlagValues
--framework <name>express, nestjs, nextjs, tanstack
--database <name>none, prisma, drizzle, mongoose
--pm <name>npm, pnpm, bun
--auth / --no-authAdd authentication
--docker / --no-dockerAdd Docker files
--rate-limit / --no-rate-limitAdd rate limiting (Express and NestJS)
--ci / --no-ciAdd a GitHub Actions workflow
--agents / --no-agentsAdd AGENTS.md for AI coding agents
--vscode / --no-vscodeAdd VS Code settings and recommended extensions
--git / --no-gitInitialize a git repository
--install / --no-installInstall dependencies
-y, --yesUse defaults for missing options, no prompts
--dry-runList what would be created, write nothing
-h, --helpShow help
-v, --versionShow the version

--database names the ORM, which also selects the database: prisma and drizzle use PostgreSQL, mongoose uses MongoDB.

With --yes, the defaults are Express, no database, no auth, no rate limiting, no Docker, git initialized, and dependencies not installed. The package manager defaults to the one that ran the CLI: pnpm for pnpm dlx, bun for bunx, and npm otherwise.

--auth with nextjs or tanstack needs a database, because Better Auth stores users and sessions. Without one the CLI stops with an error.

--yes requires a project path.

--rate-limit with nextjs or tanstack stops with an error: Better Auth already limits its own endpoints.

Cancelling a prompt (Ctrl+C or Esc) exits with code 130 and writes nothing.

Dry run

--dry-run goes through the prompts and checks as usual, then prints what the build would produce instead of building it:

  • every file, with its path inside the project
  • dependencies and dev dependencies with their versions, and any dependency overrides
  • package.json scripts
  • environment variables written to .env and .env.example (generated secrets are shown as <random secret>)
  • commands run after the files are written: the install, when --install is set, and git init, unless the project is inside an existing repository

No files are written, nothing is installed, and no repository is initialized.

Without an interactive terminal (in CI or when input is piped) the CLI cannot ask questions. Pass --yes, or give every option as a flag. Otherwise it stops with an error and a non-zero exit code.


Prompt Flow

Intro

The CLI prints its wordmark, version, and key hints, then starts the prompts. Each answered prompt collapses to one line, such as ✓ Project name my-api.

Project name

$ Project name
  my-awesome-project

You can enter a name (my-api), a nested path (apps/api), or . to use the current directory. The target directory must be empty or not exist yet. A folder that only holds .git, such as a clone of an empty repository, also works.

Rules for the folder name, which becomes the package.json name:

  • Lowercase letters, numbers, and hyphens only
  • Cannot be empty and cannot start or end with a hyphen

Valid examples: my-api, cool-app-v2, apps/customer-portal

Application type

$ Application type
  › Backend API  Express or NestJS
    Full-stack app
OptionWhat it generates
Backend APIExpress or NestJS server (prompts for framework next)
Full-stackNext.js 16 or TanStack Start (prompts for framework)

Full-stack generates a single project at the root of your chosen directory, not a separate backend/ + frontend/ monorepo.

Backend framework (Backend API only)

$ Backend framework
  › Express
    NestJS
OptionWhat it generates
ExpressExpress 5 server, tsx watch for development
NestJSNestJS 12 server (ES modules), nest start --watch

This prompt does not appear for Full-stack projects.

Frontend framework (Full-stack only)

$ Frontend framework
  › Next.js 16  App Router
    TanStack Start
OptionWhat it generates
Next.js 16App Router, next dev/next build, standalone server
TanStack StartFile-based routing, vite dev, Nitro production server

Database type

$ Database
  › None  skip database setup
    MongoDB
    PostgreSQL
ChoiceAvailability
noneBackend and Full-stack
PostgreSQLBackend and Full-stack
MongoDBBackend and full-stack (Mongoose)

ORM / ODM (shown only when a database is selected)

For PostgreSQL:

$ ORM
  › Prisma  type-safe ORM
    Drizzle
ORMAvailable in
PrismaEvery framework: Express, NestJS, Next.js, and TanStack Start
DrizzleEvery framework: Express, NestJS, Next.js, and TanStack Start

For MongoDB:

$ ORM
  › Mongoose  MongoDB ODM
ODMNotes
MongooseExpress, NestJS, Next.js, and TanStack Start

If you select none for database type, this prompt is skipped entirely.

Package manager

$ Package manager
  › npm
    pnpm
    bun

The package manager that ran the CLI is selected first: pnpm for pnpm dlx, bun for bunx, and npm otherwise. The generated dev script, install commands, and Docker image are tailored to your choice. With bun, the dev script uses bun --watch instead of tsx watch.

With pnpm, the CLI also writes pnpm-workspace.yaml. It holds the dependency overrides and approves the install scripts of bcrypt, esbuild, prisma, @prisma/engines, and unrs-resolver. Without that approval, pnpm 11 and later stop pnpm install with an "Ignored build scripts" error.

Docker

$ Add Docker?
    Yes   › No

If yes, three files are added: Dockerfile, docker-compose.yml, and .dockerignore. With a database, Compose also runs PostgreSQL or MongoDB. See Docker.

Authentication

$ Add JWT authentication?     # Express and NestJS
$ Add Better Auth?            # Next.js and TanStack Start
    Yes   › No

Express and NestJS - JWT-based auth with jsonwebtoken and bcrypt. Always prompted regardless of database selection.

Next.js / TanStack full-stack - Better Auth with a database adapter. This prompt only appears if you selected a database. If you chose none for database, auth is skipped automatically.

Rate limiting (Express and NestJS only)

$ Add rate limiting?
    Yes   › No

If yes, every route allows 100 requests per client IP in 15 minutes, and with auth, sign-up and login allow 10. Express uses express-rate-limit; NestJS uses @nestjs/throttler. Full-stack projects are not asked: Better Auth already limits its own endpoints. See rate limiting.

GitHub Actions CI

$ Add GitHub Actions CI?
    Yes   › No

If yes, the project gets .github/workflows/ci.yml. On every push to main and every pull request it installs dependencies with your package manager, then runs build, type:check, and lint on Node.js 24. The build step sets placeholders for the variables a build needs, such as DATABASE_URL, so the workflow needs no secrets and never connects to a database. Commit your lockfile to get the same versions in CI.

AI agent instructions

$ Add AGENTS.md for AI coding agents?
    Yes   › No

If yes, the project gets an AGENTS.md, the instructions file that coding agents such as Codex, Cursor, Copilot, and Gemini CLI read, and a CLAUDE.md that imports it for Claude Code. It is written for the stack you chose: the install, dev, build, type-check, and lint commands for your package manager, the database workflow, where the code lives, and rules for the project, such as validating input, keeping secrets on the server, and running type-check and lint before a task is done. It also links the official docs for each library so an agent checks them instead of guessing. Edit it as the project grows.

VS Code settings

$ Add VS Code settings?
    Yes   › No

If yes, the project gets .vscode/settings.json, which fixes ESLint problems on save, formats with Prettier on save in Express and NestJS, and uses the project's TypeScript version. TanStack Start projects also mark the generated src/routeTree.gen.ts as read-only. .vscode/extensions.json recommends the extensions for your stack, such as ESLint, Prettier or Tailwind CSS, Prisma, MongoDB, Container Tools, and GitHub Actions, and VS Code offers to install them when you open the folder. Other editors ignore both files.

Git initialization

$ Initialize a git repository?
  › Yes     No

If yes, the CLI runs git init after generating the project files. It does not create a commit, so commit the files yourself when you are ready.

If the project folder is already inside a git repository, for example apps/api in a monorepo, the CLI skips git init so you do not get a nested repository.

Install dependencies

$ Install dependencies now?
    Yes   › No

If yes, the CLI runs your chosen package manager's install command after generating the project.

Confirmation summary

Before generating, the CLI displays a summary:

  my-awesome-api
  Stack  express · prisma · auth
  Setup  npm · git
  Path   my-awesome-api

$ Create this project?
  › Yes     No

Select Yes to start generation, or No to cancel without making changes.

Generation and next steps

The CLI generates your project based on your selections, then optionally installs dependencies and initializes git.

On success you will see output similar to:

cd my-awesome-api
npm install        # if you skipped dependency install
npm run dev

After Generation

Scripts - Backend (Express)

npm run dev        # tsx watch src/index.ts (hot reload, NODE_ENV=development)
npm run build      # tsc (compile TypeScript to dist/)
npm run start      # node dist/index.js (NODE_ENV=production)
npm run lint       # eslint src
npm run format     # prettier --write "src/**/*.ts"
npm run type:check # tsc --noEmit

With database (Prisma):

npm run prisma:generate
npm run prisma:migrate
npm run prisma:studio

With database (Mongoose): no additional scripts - connection is handled in src/lib/db.ts.

With Docker:

npm run docker:build
npm run docker:up
npm run docker:down
npm run docker:logs

Scripts - Full-stack (Next.js)

npm run dev    # next dev
npm run build  # next build
npm run start  # next start
npm run lint   # eslint

Scripts - Full-stack (TanStack Start)

npm run dev         # vite dev
npm run build       # vite build (writes .output/)
npm run start       # node --env-file-if-exists=.env .output/server/index.mjs
npm run type:check  # tsc --noEmit
npm run lint        # eslint src

With database (Prisma):

npm run prisma:generate
npm run prisma:migrate
npm run prisma:studio

With database (Drizzle):

npm run db:generate
npm run db:migrate
npm run db:studio

Tips

  • The project name becomes the folder name and the package.json name.
  • Check .env before starting the server. Replace placeholder values such as database URLs with your own.
  • All generated files are plain TypeScript, so you can edit them freely.

The generated code is a starting point. Review and test it before you deploy.

On this page