Better-TS-Stack

Project Templates

What files and code better-ts-stack actually generates

This page shows what files and scripts are generated for each selection in the CLI.


Backend API Template (Express)

Selecting Backend API with Express generates:

index.ts
package.json
tsconfig.json
.env
.env.example
.eslintrc.js
eslint.config.mjs
.prettierrc
.gitignore

The base backend template generates exactly two source files: src/index.ts and src/routes/health.ts. There is no app.ts, routes/index.ts, middleware/errorHandler.ts, types/ directory, or README.md in the base output.

src/index.ts

The entry point configures Express middleware, mounts the health route, and starts the server:

import cors from "cors";
import dotenv from "dotenv";
import express, { Express, Request, Response } from "express";
import helmet from "helmet";
import morgan from "morgan";

import healthRouter from "./routes/health";

dotenv.config();

const app: Express = express();
const port = process.env.PORT || 3000;

app.use(helmet());
app.use(cors());
app.use(morgan("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use("/health", healthRouter);

// 404 handler
app.use((_req: Request, res: Response) => {
  res.status(404).json({ error: "Not Found" });
});

// Error handler
app.use((err: Error, _req: Request, res: Response) => {
  console.error(err.stack);
  res.status(500).json({ error: "Internal Server Error" });
});

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

export default app;

Generated package.json scripts

{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js",
    "lint": "eslint src",
    "lint:fix": "eslint src --fix",
    "format": "prettier --write \"src/**/*.ts\"",
    "type:check": "tsc --noEmit"
  }
}

With bun as the package manager, dev becomes bun --watch src/index.ts.


Full-stack Templates

Selecting Full-stack generates a single application at the project root. There are no separate backend/ or frontend/ subdirectories. You pick the frontend framework in the prompt.

Next.js

Generated package.json scripts

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  }
}

TanStack Start

src/
|-- routes/           # File-based routes (__root.tsx, index.tsx)
|-- routeTree.gen.ts  # Generated route tree
|-- router.tsx        # createRouter() + QueryClient + SSR query integration
|-- lib/utils.ts      # cn() helper
|-- components/ui/    # shadcn-compatible base UI primitives
|-- styles/app.css    # Tailwind CSS v4 import + base styles
vite.config.ts        # tanstackStart() + tailwind + react plugins
components.json       # shadcn CLI-compatible component config
package.json          # "type": "module"

The template uses the ~/* path alias mapped to src/*. The build produces a fetch-compatible SSR handler via Vite (no Nitro), and the start script serves it with srvx. The CLI includes the initial route tree, and TanStack Router regenerates it during Vite development and builds.

Generated package.json scripts

{
  "scripts": {
    "dev": "vite dev",
    "build": "vite build",
    "start": "srvx --prod -s ../client dist/server/server.js",
    "preview": "vite preview",
    "type:check": "tsc --noEmit",
    "lint": "eslint src"
  }
}

With Database Modules

PostgreSQL + Prisma

schema.prisma

Scripts added to package.json:

npm run prisma:generate
npm run prisma:migrate   # prisma migrate dev
npm run prisma:studio

Environment variable added: DATABASE_URL

PostgreSQL + Drizzle

schema.ts
db.ts
drizzle.config.ts

For TanStack Start, the schema and client live in src/lib/schema.ts and src/lib/db.ts (drizzle.config.ts points at ./src/lib/schema.ts).

Scripts added to package.json:

npm run db:generate  # drizzle-kit generate
npm run db:migrate   # drizzle-kit migrate
npm run db:studio    # drizzle-kit studio

Environment variable added: DATABASE_URL

If Better Auth is also enabled with Drizzle, the generated lib/schema.ts (or src/lib/schema.ts for TanStack) contains the Better Auth core tables instead of a sample table.

MongoDB + Mongoose

Environment variable added: MONGODB_URI

Mongoose adds no extra npm scripts. The connection is established in src/lib/db.ts.


With Auth Module

Environment variables added: JWT_SECRET, JWT_EXPIRES_IN

The auth router is mounted at /auth in src/index.ts.


With Docker Module

Dockerfile
docker-compose.yml
.dockerignore

The Dockerfile is framework-aware: it uses a Next.js standalone output stage for Next.js projects, an srvx-based production stage for TanStack Start, and a Node.js multi-stage build for Express backends. Both full-stack images run npm run build (and Prisma generate for the database variant) and copy the build output to a non-root Alpine production image.

Scripts added to package.json:

npm run docker:build  # docker build -t <projectName> .
npm run docker:up     # docker compose up -d
npm run docker:down   # docker compose down
npm run docker:logs   # docker compose logs -f

Supported Combinations

FrameworkApp typeDatabaseORM/ODMAuthStatus
ExpressBackendnone-JWTYes
ExpressBackendPostgreSQLPrismaJWTYes
ExpressBackendMongoDBMongooseJWTYes
Next.jsFull-stacknone--Yes
Next.jsFull-stackPostgreSQLPrismaBetter AuthYes
Next.jsFull-stackPostgreSQLDrizzleBetter AuthYes
Next.jsFull-stackMongoDBMongooseBetter AuthYes
TanStackFull-stacknone--Yes
TanStackFull-stackPostgreSQLPrismaBetter AuthYes
TanStackFull-stackPostgreSQLDrizzleBetter AuthYes
TanStackFull-stackMongoDBMongooseBetter AuthYes
-BackendNestJS--Coming soon

On this page