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.
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| Flag | Values |
|---|---|
--framework <name> | express, nestjs, nextjs, tanstack |
--database <name> | none, prisma, drizzle, mongoose |
--pm <name> | npm, pnpm, bun |
--auth / --no-auth | Add authentication |
--docker / --no-docker | Add Docker files |
--rate-limit / --no-rate-limit | Add rate limiting (Express and NestJS) |
--ci / --no-ci | Add a GitHub Actions workflow |
--agents / --no-agents | Add AGENTS.md for AI coding agents |
--vscode / --no-vscode | Add VS Code settings and recommended extensions |
--git / --no-git | Initialize a git repository |
--install / --no-install | Install dependencies |
-y, --yes | Use defaults for missing options, no prompts |
--dry-run | List what would be created, write nothing |
-h, --help | Show help |
-v, --version | Show 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.jsonscripts- environment variables written to
.envand.env.example(generated secrets are shown as<random secret>) - commands run after the files are written: the install, when
--installis set, andgit 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-projectYou 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| Option | What it generates |
|---|---|
| Backend API | Express or NestJS server (prompts for framework next) |
| Full-stack | Next.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| Option | What it generates |
|---|---|
| Express | Express 5 server, tsx watch for development |
| NestJS | NestJS 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| Option | What it generates |
|---|---|
| Next.js 16 | App Router, next dev/next build, standalone server |
| TanStack Start | File-based routing, vite dev, Nitro production server |
Database type
$ Database
› None skip database setup
MongoDB
PostgreSQL| Choice | Availability |
|---|---|
| none | Backend and Full-stack |
| PostgreSQL | Backend and Full-stack |
| MongoDB | Backend and full-stack (Mongoose) |
ORM / ODM (shown only when a database is selected)
For PostgreSQL:
$ ORM
› Prisma type-safe ORM
Drizzle| ORM | Available in |
|---|---|
| Prisma | Every framework: Express, NestJS, Next.js, and TanStack Start |
| Drizzle | Every framework: Express, NestJS, Next.js, and TanStack Start |
For MongoDB:
$ ORM
› Mongoose MongoDB ODM| ODM | Notes |
|---|---|
| Mongoose | Express, NestJS, Next.js, and TanStack Start |
If you select none for database type, this prompt is skipped entirely.
Package manager
$ Package manager
› npm
pnpm
bunThe 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 › NoIf 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 › NoExpress 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 › NoIf 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 › NoIf 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 › NoIf 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 › NoIf 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 NoIf 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 › NoIf 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 NoSelect 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 devAfter 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 --noEmitWith database (Prisma):
npm run prisma:generate
npm run prisma:migrate
npm run prisma:studioWith 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:logsScripts - Full-stack (Next.js)
npm run dev # next dev
npm run build # next build
npm run start # next start
npm run lint # eslintScripts - 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 srcWith database (Prisma):
npm run prisma:generate
npm run prisma:migrate
npm run prisma:studioWith database (Drizzle):
npm run db:generate
npm run db:migrate
npm run db:studioTips
- The project name becomes the folder name and the
package.jsonname. - Check
.envbefore 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.