Better-TS-Stack

Upgrade notes

What changed in each release and what to do when upgrading

2.1.2

Adds instructions for AI coding agents and VS Code settings, and moves Next.js projects to the November security release. Projects you already generated are not changed.

New

  • VS Code settings. Add them with --vscode. .vscode/settings.json fixes ESLint problems on save and uses the project's TypeScript, and .vscode/extensions.json recommends the extensions for your stack. See VS Code settings.
  • AGENTS.md for AI coding agents. Add it with --agents or the new "Add AGENTS.md for AI coding agents?" prompt. It tells agents such as Codex, Cursor, Copilot, and Gemini CLI how your project works: the commands for your package manager, the database workflow, where the code lives, and rules to follow, such as validating input, keeping secrets on the server, and passing type-check and lint before a task is done. It links the official docs for each library in your stack, so an agent checks them instead of guessing. A CLAUDE.md imports it for Claude Code. It is off unless you choose it. See AI agent instructions.

Security

  • New Next.js projects require Next.js 16.4.5, which fixes the vulnerabilities in the November 3 security release, including a high-severity cache disclosure. New projects already resolved it through their version range; the minimum is now raised.
  • Update existing Next.js projects: run npm install next@latest, then rebuild and redeploy. On Netlify, a project with proxy.ts also needs the adapter update described in Netlify's notice.

Changed in generated projects

  • Express and NestJS keep .vscode/settings.json and .vscode/extensions.json in git. .gitignore now ignores the rest of .vscode/ instead of the whole folder.
  • .dockerignore leaves out AGENTS.md and CLAUDE.md, which the image does not need.

For contributors

  • Publishing a GitHub release publishes the CLI to npm from GitHub Actions (.github/workflows/publish.yml), through npm trusted publishing with provenance and no stored token.
  • docker-matrix.cjs publishes PostgreSQL on port 45432. Windows reserves blocks of ports above 49152 for Hyper-V and WSL, and the old port 54329 was in one, so the database container could not start.
  • The matrix scripts pass each command as one string, which removes the Node.js deprecation warning about arguments passed with shell.
  • generate-matrix.cjs checks that AGENTS.md is written only when chosen and renders without leftover template tags.

2.1.1

Adds a GitHub Actions workflow, and fixes security issues and bugs. Projects you already generated are not changed.

New

  • GitHub Actions CI. Add a workflow with --ci or the new "Add GitHub Actions CI?" prompt. On every push to main and every pull request it installs dependencies with your package manager, then builds, type-checks, and lints on Node.js 24. It needs no secrets. It is off unless you choose it. See GitHub Actions CI.

Security

  • New projects require patched dependencies, including Next.js 16.4.3, past the October 14 security update with its critical remote code execution fixes, and Better Auth 1.7.14. New projects already resolved these through their version ranges; the minimum versions are now raised.
  • Update existing Next.js projects now: run npm install next@latest, then rebuild and redeploy. Next.js plans another security release on November 3, 2026, so update again after that.
  • Update existing Better Auth projects with npm install better-auth@latest.
  • The CLI itself moves to Handlebars 4.7.10 and Zod 4.6.7, which fix reported vulnerabilities.

Fixed

  • npm run preview works in TanStack Start projects. It returned a 500 since the move to Nitro in 2.1.0.
  • Rate limiting no longer counts /health. Frequent health checks could use up the limit and get 429, which can make a host restart the app. To fix an existing project, skip /health in apiLimiter (Express) or add @SkipThrottle() to HealthController (NestJS).
  • Prisma projects build without a .env file, for example in CI or on a host. prisma generate failed because DATABASE_URL was missing. In an existing project, change url: env("DATABASE_URL") to url: process.env.DATABASE_URL ?? "" in prisma.config.ts.
  • The generated Prisma client is not committed. It is now in .gitignore. In an existing project, add src/generated/ (Express, NestJS) or /generated/ (Next.js, TanStack Start), then run git rm -r --cached on that folder.
  • A project named db gets a valid Compose file. Its app service had the same name as the database service. The app service is now always called app.
  • An empty project name is rejected at the prompt with a clear message.
  • .env no longer says to copy itself to .env. That line stays in .env.example.
  • The README of a generated project lists rate limiting.
  • Next steps remind you to start PostgreSQL when Compose does not run it.
  • Declining "Create this project?" says that nothing was created.

Changed in generated projects

  • Next.js projects no longer get PORT in .env. Next.js ignored it there. Use PORT=3001 npm run dev instead.
  • proxy.ts is only added to Next.js projects with auth. Without auth it did nothing on every request.
  • Express no longer logs an error for each invalid JWT, which any client could trigger. It still answers 401.
  • All dependencies move to their current releases within the same major version. TypeScript stays on 6 because typescript-eslint does not support 7 yet, and Prisma stays on 7 because version 8 is still a release candidate and Better Auth does not support it yet.

Docs

  • Corrected the Compose section of the configuration page, the TanStack Start build description in the README, and the description of proxy.ts.

For contributors

  • CI also runs on pushes to main, because releases go straight there.
  • The smoke checks verify that /health is never rate limited.

2.1.0

Projects you already generated are not changed.

New

  • Rate limiting. Express and NestJS projects can add per-IP request limits with --rate-limit or the new "Add rate limiting?" prompt: 100 requests per 15 minutes on every route, and 10 sign-up or login attempts with auth. It is off unless you choose it. See rate limiting.
  • A README in every project, with the chosen stack, setup steps, scripts, and environment variables.
  • Docker uses your package manager. The image installs with npm, pnpm, or bun to match the project, and uses the committed lockfile when there is one.
  • A database in Docker Compose. With Prisma or Drizzle, Compose runs PostgreSQL; with Mongoose, MongoDB as a replica set. The app waits for it to be healthy. See Docker.
  • Docker health checks. Images check /health (Express and NestJS) or / (Next.js and TanStack Start).
  • Environment validation. Express and NestJS projects check their environment variables with Zod in src/env.ts at startup and stop with a clear message when one is missing or malformed.
  • CORS_ORIGIN. Express and NestJS read the allowed origins from .env. The default * allows every origin, as before.
  • TanStack Start runs on Nitro in production, as TanStack's hosting guide recommends for Vite. vite build writes a self-contained server to .output/, and npm start runs node --env-file-if-exists=.env .output/server/index.mjs. srvx is gone, and so is the Windows problem where Node.js 24 before 24.20 returned 404 for CSS and JS. The Docker image runs .output/ without a node_modules install. vite dev is unchanged.
  • Graceful shutdown in Express. On SIGTERM or Ctrl+C the server finishes open requests and closes the database connection before exiting.

Changed

  • The package manager defaults to the one that ran the CLI: pnpm dlx better-ts-stack selects pnpm, and bunx selects bun. With --yes this is the default too; npx still selects npm.
  • A folder that only holds .git can be used, for example a clone of an empty repository. A failed build leaves .git in place.
  • Cancelling a prompt exits with code 130 instead of 0.
  • Next steps mention docker compose up -d db and docker:up when Docker is selected.
  • Home page: the docs button on the Next.js and TanStack Start home page links to the docs site, and Mongoose is listed with the other databases.
  • TanStack Start projects require Node.js 24 like the others (engines was >=24.20.0).
  • Express and NestJS JWT_SECRET must be at least 32 characters. The generated value already is; a short value now stops the server at startup.

For contributors

  • ProjectConfig has a useRateLimit field, and the new rate-limit/express and rate-limit/nestjs modules hold the rate-limit code.
  • The template matrix covers rate limiting for Express and NestJS, and the smoke checks verify the rate-limit headers and the 429 after 10 login attempts.

2.0.2

Projects you already generated are not changed. To patch an existing TanStack Start project, update @tanstack/react-start to 1.168.60 or later, then rebuild and redeploy.

TanStack Start 1.0

  • TanStack Start is stable. The CLI no longer labels it a release candidate.
  • New TanStack Start projects require @tanstack/react-start 1.169.3 or later. This is past the fix for CVE-2026-102989, a cross-site scripting bug in server-function responses fixed in 1.168.60. @tanstack/react-router, the router devtools and SSR query packages, TanStack Query, srvx, Vite, and @vitejs/plugin-react move to their current releases.

Fixed

  • The cd step is quoted when the project path has spaces, so it can be pasted as it is.
  • Drizzle updatedAt columns update on every write. Before, they kept the creation time.

Changed in generated projects

  • The Better Auth forms in Next.js and TanStack Start use z.email() instead of the deprecated z.string().email().

Docs

  • The prompt examples in usage show the prompts as the CLI prints them since 2.0.0, and NestJS is listed wherever Express is.

2.0.1

A more useful --dry-run, bug fixes, and cleanup. Projects you already generated are not changed.

New

  • --dry-run lists what a build would produce. After the summary it prints every file, the dependencies and dev dependencies with their versions, dependency overrides, package.json scripts, environment variables, and the commands run afterwards (install, git init). Before, it only repeated the summary. Nothing is written, installed, or initialized. See dry run.

Fixed

  • No terminal, no silent exit. Run without an interactive terminal and without --yes, the CLI used to print a prompt, exit with code 0, and create nothing. It now stops with an error and a non-zero exit code.
  • No nested git repository. Creating a project inside an existing repository, such as apps/api in a monorepo, no longer runs git init.
  • Express with Mongoose prints the server address when it starts, like every other project.
  • TanStack Start with Drizzle pins esbuild to 0.28.2, like the other Drizzle projects. Without it, drizzle-kit installed an old esbuild that npm audit reports.
  • Project name rules in the troubleshooting page now match the CLI: lowercase letters, numbers, and hyphens.

Changed in generated projects

  • Drizzle's default DATABASE_URL is postgresql://postgres:postgres@localhost:5432/postgres, the same as Prisma's. Before it was postgresql://user:password@localhost:5432/mydb?schema=public.
  • Express Prettier config no longer loads prettier-plugin-tailwindcss or sorts React and Next.js imports first, and the package is no longer installed.
  • Next.js and TanStack Start auth no longer list mongodb as a dependency unless you chose Mongoose.
  • zod is ^4.6.5 in every auth module.

For contributors

  • Every template file ending in .hbs is rendered automatically. The templateFiles list in config.json is gone.
  • The published package no longer ships type declarations or source maps, and it includes a LICENSE file.
  • BuildResult.success, BuildError.exitCode, and the lowercase, uppercase, and kebabCase template helpers were removed. None were used.

2.0.0

Upgrading the CLI only changes projects you generate from now on. It does not change a project you already generated.

New

  • NestJS backend. NestJS 12 is a second backend framework, with the same Prisma, Drizzle, Mongoose, JWT auth, and Docker options as Express. See the backend docs.
  • Flags. Every prompt has a flag, and --yes runs the CLI without prompts. --dry-run, --help, and --version were added. See flags.
  • Project paths. The project can be created in a nested path (apps/api) or in the current directory (.).
  • Request validation. Express JWT auth validates request bodies with Zod.
  • New look. The CLI has a new design: a wordmark banner, $ prompts that collapse to one ✓ line when answered, a compact summary, and highlighted next steps. An interactive run starts on a cleared screen; runs with --yes do not clear it.

Changed behavior

  • The email must be a valid address. POST /auth/register and POST /auth/login in new Express projects return 400 for a malformed email. Before, any non-empty string was accepted.
  • A failed build cleans up after itself. If generation fails, the partial project folder is removed, so you can run the command again. A folder that already existed is emptied instead of deleted.
  • The target directory is checked first. A non-empty directory is reported right after you enter the project name, not after the last prompt.
  • Generated Express projects no longer include .eslintrc.js. ESLint 10 ignored that file; eslint.config.mjs is the config in use.
  • Docker images for backend projects run as appuser (was expressuser).

For contributors

ProjectConfig no longer has databaseType and orm. The database field (none, prisma, drizzle, or mongoose) is the only database setting. This matters if you call the builder from your own scripts.

CI runs lint, type-check, and the template matrix on Linux and Windows for every push and pull request.

Testing

npm run test:templates
node scripts/check-matrix.cjs <matrix.json> <express|nestjs|nextjs|tanstack>
node scripts/smoke-matrix.cjs <matrix.json> <express|nestjs|nextjs|tanstack>
node scripts/docker-matrix.cjs <matrix.json> <express|nestjs|nextjs|tanstack>

docker-matrix.cjs needs a running Docker daemon. It builds the image of each Docker project, runs it next to a real PostgreSQL or MongoDB container, and repeats the HTTP checks.

test:templates prints the path of the matrix file to use. Pass a directory to choose where it goes: npm run test:templates -- <dir>.

1.0.8

This release requires Node.js 24 or later, for both the CLI and the projects it generates.

Upgrading the CLI only changes projects you generate from now on. It does not change the code or database of a project you already generated.

Dependency updates

Dependencies in the CLI, the docs site, and the templates were updated to their latest stable versions. The main upgrades:

PackageVersion
Next.js16.3.4
React19.2.8
Express5.2.1
Prisma7.10.0
Drizzle ORM0.45.2
Mongoose9.9.5
Better Auth1.7.3
Zod4.5.4
Tailwind CSS4.3.3
ESLint10.10.0

Some packages are held back on purpose:

  • TypeScript stays on 6.0.3. typescript-eslint 8.69.0 only supports TypeScript below 6.1, so TypeScript 7 can't be used yet.
  • @types/node stays on 24.x (24.13.3), to match the minimum Node.js version.
  • Prisma stays on 7.10.0. Prisma 8 was still a release candidate.

Security overrides

Generated projects pin a few transitive dependencies to versions without known advisories:

  • Prisma projects: deepmerge-ts 8.0.2 and mysql2 3.24.3
  • Drizzle projects: esbuild 0.28.2

The overrides are written for npm, pnpm, and Bun. Check whether they are still needed when you upgrade Prisma or Drizzle.

Template fixes

  • Express uses version 5 and its request types. The error handler takes all four parameters, and dotenv loads before the auth modules.
  • Express now supports Drizzle, including JWT users stored in the database and migration scripts.
  • MongoDB projects use Mongoose. The old MongoDB + Prisma option generated a PostgreSQL schema that didn't work.
  • Express with Mongoose connects to MongoDB before the server starts listening. The optional user name now matches the registration API.
  • Prisma generates its client before each build, and the output folders match the imports. The seed script, which pointed at a file that didn't exist, was removed.
  • Better Auth 1.7.3 account tables use a unique providerId/accountId pair again. If your app used the issuer field from Better Auth 1.7.0 to 1.7.2, follow the Better Auth migration guide.
  • The Next.js Drizzle client now receives the schema, so Better Auth can find its tables.
  • Each generated project gets its own random auth secret in .env. Set your own secrets and database URLs for production.
  • Express router types are explicit, so declaration output also works with pnpm.
  • Express projects generated with pnpm now start correctly.
  • Docker images use Node.js 24 and Compose v2, load environment variables at runtime, and stop on build errors.

Testing

Every supported combination of framework, database, auth, package manager, and Docker (90 in total) was generated. Each of the 15 distinct npm stacks was installed, built, type-checked, linted, and smoke-tested over HTTP, including sign-up, login, and protected routes against real PostgreSQL and MongoDB databases. Representative projects were also built with pnpm, Bun, and Docker.

To run the same checks from the repository root:

npm run test:templates
node scripts/check-matrix.cjs <matrix.json> express
node scripts/check-matrix.cjs <matrix.json> nextjs

test:templates prints the path of the matrix file to use.

Next.js with MongoDB and Better Auth needs some database setup first. See the database docs.

Publishing (maintainers)

From the repository root:

npm ci
npm run build
npm run lint
npm run type:check
npm pack --workspace better-ts-stack --dry-run
npm login
npm publish --workspace better-ts-stack --access public --tag latest

The prepack script rebuilds the CLI, and the published package contains the compiled CLI and the templates.

On this page