Express.js Backend Guide#
A practical guide to building production-ready Express.js applications with TypeScript.
🎯 What You'll Learn#
- Server Architecture - Structured Express app with middleware
- Error Handling - Centralized error handling with proper logging
- Authentication - Protect routes with Better Auth
- Validation - Type-safe request validation with Zod
- Database - Drizzle ORM for type-safe database queries
- Logging - Winston logger setup for debugging and monitoring
📚 Sections#
1. Server Setup#
Learn how to structure an Express server with CORS, middleware, and routers.
2. Error Handling#
Centralized error handling, status codes, and logging errors.
3. Authentication#
Protect routes, check roles, and handle sessions with Better Auth.
4. Request Validation#
Validate req.body, req.query, and req.params with Zod schemas.
5. Complete Request Flow#
See how a complete request flows through middleware, validation, auth, controller, and database.
🚀 Quick Start#
# Install dependencies
npm install express cors morgan http-status-codes
npm install -D @types/express @types/cors @types/morgan
# TypeScript & validation
npm install typescript zod
npm install -D ts-node
# Logging
npm install winston
# Database (optional)
npm install drizzle-orm postgres
📖 Basic Example#
import express from "express";
import type { Request, Response } from "express";
const app = express();
// Middleware
app.use(express.json());
// Routes
app.get("/api/users", (req: Request, res: Response) => {
res.json({ users: [] });
});
// Start server
app.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
🎓 Core Concepts#
Status Codes (http-status-codes)#
import { StatusCodes } from "http-status-codes";
res.status(StatusCodes.OK).json({ data }); // 200
res.status(StatusCodes.CREATED).json({ user }); // 201
res.status(StatusCodes.BAD_REQUEST).json({ error }); // 400
res.status(StatusCodes.UNAUTHORIZED).json({ error }); // 401
res.status(StatusCodes.FORBIDDEN).json({ error }); // 403
res.status(StatusCodes.NOT_FOUND).json({ error }); // 404
res.status(StatusCodes.CONFLICT).json({ error }); // 409
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({}); // 500
Type Imports#
import type { Request, Response, NextFunction } from "express";
Environment Variables#
import "dotenv/config"; // Loads .env file automatically
Export Patterns#
| Feature | export default |
named export |
|---|---|---|
| Number allowed | Only 1 | Many |
| Import style | import x from |
import { x } from |
| Renaming | Easy: import whatever from |
Must rename: import { x as y } |
| Common use | Configs, single classes | Utils, services, constants |
🛠️ Helper Functions#
// Get client IP (handles proxies)
export function getClientIp(req: Request): string {
if (req.headers["x-forwarded-for"]) {
return (req.headers["x-forwarded-for"] as string).split(",")[0]!.trim();
}
if (req.headers["x-real-ip"]) {
return req.headers["x-real-ip"] as string;
}
return req.ip!;
}
// Create URL-friendly slug
export function createSlug(title: string): string {
return title
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-");
}
// Generate random string
export function randString(): string {
return crypto.randomBytes(20).toString("hex");
}
// Common API responses
export const commonResps = {
internalError: {
error: "Failed to complete request due to internal error",
code: "internal_server_error",
},
};
TypeScript Patterns#
Type Assertion#
const value as string // Tells TS this is guaranteed to be a string
Filter Undefined Values#
export function filterUndefined<T>(obj: T): {
[K in keyof T]: Exclude<T[K], undefined>;
} {
const result = {} as any;
for (const key in obj) {
if (obj[key] !== undefined) {
result[key] = obj[key];
}
}
return result;
}
Ignore TypeScript Errors (use sparingly!)#
// @ts-ignore
const x: number = "this is actually a string";
📋 Next Steps#
Start with Server Setup to build your Express application structure, then move through each section to add functionality.
Pro Tip: Follow the request flow diagram in the Complete Request Flow section to understand how all pieces work together.