π A production-ready, batteries-included TypeScript + Express.js boilerplate with modern tooling, structured logging, request validation, rate limiting, and more.
TS-BoilerplateX gives developers a robust starting point for building powerful REST APIs and web applications. It combines TypeScript's static typing with Express.js 5's flexibility, pre-configured with industry best practices for security, observability, and developer experience.
| Category | Feature | Details |
|---|---|---|
| ποΈ Core | Express.js 5 | Latest version with improved routing & async error handling |
| π Language | TypeScript 5.9+ | Strict mode, ES2022 target, full type safety |
| π Security | Helmet | Sets secure HTTP headers automatically |
| π Security | CORS | Configurable cross-origin resource sharing |
| π Security | Rate Limiting | Two-tier: global + strict (for auth endpoints) |
| π Logging | Pino | Structured JSON logging (prod) / pretty print (dev) |
| π Tracing | Request ID | UUID-based request tracking via X-Request-Id header |
| β Validation | Zod v4 | Schema-based request validation middleware |
| π Health | Health Check | /api/health endpoint with uptime & memory stats |
| π‘οΈ Resilience | Graceful Shutdown | Clean SIGTERM/SIGINT handling with connection draining |
| π‘οΈ Resilience | Error Handling | Global error handler + uncaught exception/rejection capture |
| π§Ή Linting | ESLint 10 | Flat config with typescript-eslint |
| π¦ Bundling | esbuild | Lightning-fast production builds |
| π Docs | TypeDoc | Auto-generated API documentation |
| π DX | Nodemon | Hot-reload during development |
- Node.js >= 18.0.0
- npm >= 9.0.0
npx ts-boilerplatex# Clone the repository
git clone https://github.com/eldhopaulose/TS-BoilerplateX.git
cd TS-BoilerplateX
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env
# Start development server
npm run devThe server will start at http://localhost:3000 with hot-reload enabled.
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Server port |
NODE_ENV |
development |
Environment (development / production) |
LOG_LEVEL |
debug (dev) / info (prod) |
Pino log level |
RATE_LIMIT_WINDOW_MS |
900000 |
Rate limit window (ms) β 15 min |
RATE_LIMIT_MAX |
100 |
Max requests per window (global) |
STRICT_RATE_LIMIT_WINDOW_MS |
900000 |
Strict rate limit window (ms) |
STRICT_RATE_LIMIT_MAX |
10 |
Max requests per window (strict) |
TS-BoilerplateX/
βββ src/
β βββ bin/
β β βββ server.ts # HTTP server creation & lifecycle
β βββ controller/
β β βββ indexController.ts # Root route handler (API index)
β β βββ userController.ts # User CRUD handlers with logging
β βββ middleware/
β β βββ index.ts # Barrel export for all middleware
β β βββ rateLimiter.ts # Global & strict rate limiters
β β βββ requestId.ts # UUID-based request ID tracking
β β βββ validate.ts # Zod schema validation middleware
β βββ models/
β β βββ user.model.ts # User data model (in-memory store)
β βββ routes/
β β βββ health.ts # Health check endpoint
β β βββ index.ts # Root routes
β β βββ user.routes.ts # User CRUD routes with validation
β βββ schemas/
β β βββ user.schema.ts # Zod validation schemas for User API
β βββ types/
β β βββ express.d.ts # Express type augmentations
β βββ utils/
β β βββ logger.ts # Pino logger configuration
β βββ main.ts # Application entry point
βββ .env.example # Environment variable template
βββ eslint.config.mjs # ESLint 10 flat config
βββ nodemon.json # Development server config
βββ package.json
βββ tsconfig.json # TypeScript compiler config
βββ README.md
| Script | Description |
|---|---|
npm run dev |
Start dev server with nodemon (hot-reload) |
npm run start |
Start with ts-node (no compilation needed) |
npm run production |
Run compiled JS from dist/ |
npm run build |
Compile TypeScript to JavaScript |
npm run build-all |
Clean, compile, and bundle with esbuild |
npm run lint |
Run ESLint on source files |
npm run lint:fix |
Run ESLint with auto-fix |
npm run test |
Run tests with Jest |
npm run docs |
Generate API docs with TypeDoc |
npm run clean |
Remove build artifacts |
The boilerplate ships with a fully working User CRUD API that demonstrates all features. Start the server and try:
| Method | Endpoint | Description | Features Used |
|---|---|---|---|
GET |
/ |
API index (lists all routes) | β |
GET |
/api/health |
Health check | Memory stats, uptime |
GET |
/api/users |
List all users (paginated) | Zod query validation |
GET |
/api/users/stats |
User statistics | Aggregation |
GET |
/api/users/:id |
Get user by ID | Zod UUID validation |
POST |
/api/users |
Create a user | Zod body validation, strict rate limit |
PUT |
/api/users/:id |
Update a user | Zod partial validation |
DELETE |
/api/users/:id |
Delete a user | Strict rate limit |
1. List all users (with pagination & filtering):
curl http://localhost:3000/api/users?page=1&limit=10
# Filter by role
curl http://localhost:3000/api/users?role=admin
# Filter by active status
curl http://localhost:3000/api/users?isActive=trueResponse:
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Eldho Paulose",
"email": "eldho@example.com",
"age": 28,
"role": "admin",
"isActive": true,
"createdAt": "2026-03-27T12:00:00.000Z",
"updatedAt": "2026-03-27T12:00:00.000Z"
}
],
"pagination": {
"total": 3,
"page": 1,
"limit": 10,
"totalPages": 1,
"hasNextPage": false,
"hasPrevPage": false
}
}2. Create a new user:
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 30,
"role": "moderator"
}'Response (201):
{
"success": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 30,
"role": "moderator",
"isActive": true,
"createdAt": "2026-03-27T12:01:00.000Z",
"updatedAt": "2026-03-27T12:01:00.000Z"
}
}3. Validation error example (missing required fields):
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name": "A"}'Response (422):
{
"error": "Validation Error",
"details": [
{ "field": "body.name", "message": "Name must be at least 2 characters" },
{ "field": "body.email", "message": "Required" },
{ "field": "body.age", "message": "Required" }
]
}4. Update a user:
curl -X PUT http://localhost:3000/api/users/<user-id> \
-H "Content-Type: application/json" \
-d '{"name": "Alice Smith", "age": 31}'5. Delete a user:
curl -X DELETE http://localhost:3000/api/users/<user-id>6. Get user statistics:
curl http://localhost:3000/api/users/statsResponse:
{
"success": true,
"data": {
"total": 3,
"active": 2,
"inactive": 1,
"byRole": { "admin": 1, "user": 1, "moderator": 1 },
"averageAge": 28
}
}7. Health check:
curl http://localhost:3000/api/healthPino provides ultra-fast, structured JSON logging in production and colorful, readable output in development.
import logger from "./utils/logger";
logger.info("Server started");
logger.error({ err: error }, "Database connection failed");
logger.debug({ userId: 123 }, "User fetched");Development output:
[12:34:56] INFO: Incoming request
method: "GET"
url: "/api/health"
requestId: "550e8400-e29b-41d4-a716-446655440000"
Production output (JSON):
{"level":"info","time":"2026-03-27T12:34:56.789Z","method":"GET","url":"/api/health","requestId":"550e8400-e29b-41d4-a716-446655440000","msg":"Incoming request"}Type-safe request validation using Zod schemas:
import { z } from "zod";
import { validate } from "../middleware/validate";
const createUserSchema = z.object({
body: z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
age: z.number().int().min(18, "Must be at least 18"),
}),
});
router.post("/users", validate(createUserSchema), userController.create);Validation error response (422):
{
"error": "Validation Error",
"details": [
{ "field": "body.email", "message": "Invalid email address" },
{ "field": "body.age", "message": "Must be at least 18" }
]
}Two-tier rate limiting out of the box:
import { globalRateLimiter, strictRateLimiter } from "../middleware";
// Global: applied to all routes (100 req/15min by default)
app.use(globalRateLimiter);
// Strict: for sensitive endpoints (10 req/15min by default)
router.post("/auth/login", strictRateLimiter, authController.login);Every request gets a unique UUID via the X-Request-Id header. Pass an existing ID from upstream services, or one will be auto-generated:
# Auto-generated
curl http://localhost:3000/
# Response header: X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
# Pass your own
curl -H "X-Request-Id: my-trace-id" http://localhost:3000/
# Response header: X-Request-Id: my-trace-idBuilt-in health check endpoint for load balancers and container orchestration:
curl http://localhost:3000/api/health{
"status": "ok",
"uptime": 123.456,
"timestamp": "2026-03-27T12:34:56.789Z",
"memoryUsage": {
"rss": "45.23 MB",
"heapUsed": "22.11 MB",
"heapTotal": "35.50 MB"
},
"environment": "development"
}The server handles SIGTERM and SIGINT signals gracefully, allowing in-flight requests to complete before shutting down (with a 10-second timeout):
// Automatically handled β no configuration needed
// Logs: "Received SIGTERM. Shutting down gracefully..."
// Logs: "HTTP server closed."The following middleware is applied in order:
- Helmet β Sets secure HTTP headers (XSS protection, CSP, etc.)
- CORS β Enables cross-origin requests
- Rate Limiter β Prevents abuse with configurable limits
- Request ID β Assigns unique trace IDs to each request
- Pino Logger β Logs every incoming request with method, URL, and trace ID
- Body Parser β Parses JSON and URL-encoded bodies (up to 10MB)
- Compression β Gzip compresses response bodies
- Cookie Parser β Parses cookies from request headers
| Package | Version | Purpose |
|---|---|---|
| Express.js | 5.x | Web framework |
| TypeScript | 5.9+ | Type-safe JavaScript |
| Pino | 10.x | Structured logging |
| Zod | 4.x | Schema validation |
| Helmet | 8.x | Security headers |
| express-rate-limit | 8.x | Rate limiting |
| ESLint | 10.x | Code linting |
| esbuild | 0.27+ | Fast bundling |
| TypeDoc | 0.28+ | API documentation |
| Nodemon | 3.x | Development hot-reload |
Contributions are welcome! Feel free to open an issue or submit a pull request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Licensed under the Apache-2.0. See the LICENSE file for details.
Made with β€οΈ by Eldho Paulose