Better-TS-Stack
Modules

Backend Module

Express.js backend with TypeScript, routing, and middleware

Overview

The Backend module generates a production-ready Express.js server with TypeScript. It is the foundation of every Backend API project.

What You Get

src/
|-- index.ts          # Entry point - configures middleware, mounts routes, starts server
\-- routes/
    \-- health.ts     # GET /health endpoint

The base backend template generates exactly these two source files. No app.ts, routes/index.ts, middleware/errorHandler.ts, or types/ directory are included in the base output. Additional source files are added by optional modules (database, auth).


Generated Files

src/index.ts

The entry point configures and starts the Express app:

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;

When the auth module is selected, src/index.ts additionally imports and mounts the auth router at /auth.

src/routes/health.ts

A simple GET /health endpoint:

import { Router } from "express";

const router = Router();

router.get("/", (_req, res) => {
  res.json({ status: "ok" });
});

export default router;

Commands

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

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


Dependencies

PackageRole
expressHTTP framework
corsCross-origin resource sharing
helmetSecurity headers
morganHTTP request logger
dotenvLoads .env into process.env

Environment Variables

NODE_ENV=development
PORT=3000

Additional variables are added by the database and auth modules.


Integration with Other Modules

+ Database module

If a database module is selected, the build adds:

  • A database client singleton (src/lib/prisma.ts or src/lib/db.ts)
  • The relevant ORM package and scripts

+ Auth module

If auth is selected, the build adds:

  • src/lib/jwt.ts - JWT sign/verify utilities
  • src/middleware/requireAuth.ts - Bearer token validation middleware
  • src/services/userStore.ts - User lookup helper
  • src/controllers/authController.ts - Register/login handlers
  • src/routes/auth.ts - Router mounted at /auth
  • JWT_SECRET and JWT_EXPIRES_IN environment variables

+ Docker module

If Docker is selected, the build adds Dockerfile, docker-compose.yml, and .dockerignore plus four docker:* scripts.


Extending the Backend

Add a route

  1. Create src/routes/users.ts
  2. Implement your route handlers
  3. Register the router in src/index.ts:
import usersRouter from "./routes/users";

app.use("/users", usersRouter);

Use the requireAuth middleware

import { Router } from "express";

import { requireAuth } from "../middleware/requireAuth";

const router = Router();

router.get("/profile", requireAuth, (req, res) => {
  res.json({ message: "Protected route" });
});

export default router;

The middleware is named requireAuth in the generated file, not authMiddleware.

NestJS (coming soon)

NestJS appears in the framework selection prompt but displays a warning if selected and re-prompts. It is not yet generated.

On this page