Build type-safe backend APIs with Express or NestJS — full TypeScript on the server side.
Setting up TypeScript in a Node.js project from scratch.
# Init project & install dependencies npm init -y npm install typescript ts-node @types/node --save-dev npx tsc --init # Add dev script to package.json # "dev": "ts-node src/index.ts" # "build": "tsc" # Or use tsx for faster restarts npm install tsx --save-dev # "dev": "tsx watch src/index.ts"
The most popular Node.js framework — with full TypeScript support via @types/express.
npm install express npm install @types/express --save-dev
import express, { Request, Response, NextFunction } from 'express'; const app = express(); app.use(express.json()); // Type your data shapes interface User { id: number; name: string; email: string; } const users: User[] = [ { id: 1, name: "Alice", email: "[email protected]" }, ]; // GET all users app.get('/users', (req: Request, res: Response) => { res.json(users); }); // GET user by ID — typed route params app.get('/users/:id', (req: Request<{ id: string }>, res: Response) => { const user = users.find(u => u.id === +req.params.id); if (!user) return res.status(404).json({ error: 'Not found' }); res.json(user); }); // POST create user — typed request body app.post('/users', (req: Request<{}, {}, Omit<User, 'id'>>, res: Response) => { const newUser: User = { id: users.length + 1, ...req.body }; users.push(newUser); res.status(201).json(newUser); }); app.listen(3000, () => console.log('Server running on :3000'));
Extend Express types to add type safety for params, body, and query.
import { Request } from 'express'; // Typed query params: /search?q=alice&page=2 interface SearchQuery { q?: string; page?: string; } // Typed route params: /users/:id interface UserParams { id: string; } // Full typed request: Request<Params, ResBody, ReqBody, Query> type SearchRequest = Request<{}, {}, {}, SearchQuery>; app.get('/search', (req: SearchRequest, res: Response) => { const { q, page = '1' } = req.query; // q is string | undefined ✅ // page is string | undefined ✅ });
import { Request, Response, NextFunction } from 'express'; // Extend Request to include user data declare global { namespace Express { interface Request { user?: { id: number; role: string }; } } } export function authMiddleware( req: Request, res: Response, next: NextFunction ): void { const token = req.headers.authorization; if (!token) { res.status(401).json({ error: 'Unauthorized' }); return; } // attach user to request req.user = { id: 1, role: 'admin' }; next(); } // Now req.user is typed everywhere ✅ app.get('/profile', authMiddleware, (req: Request, res: Response) => { res.json(req.user); // { id: number; role: string } | undefined });
NestJS is built specifically for TypeScript. It uses decorators, modules, and dependency injection — giving you a structured, scalable architecture.
npm install -g @nestjs/cli nest new my-api cd my-api && npm run start:dev
import { Controller, Get, Post, Body, Param, Delete } from '@nestjs/common'; import { UsersService } from './users.service'; import { CreateUserDto } from './dto/create-user.dto'; @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Get() findAll() { return this.usersService.findAll(); } @Get(':id') findOne(@Param('id') id: string) { return this.usersService.findOne(+id); } @Post() create(@Body() dto: CreateUserDto) { return this.usersService.create(dto); } @Delete(':id') remove(@Param('id') id: string) { return this.usersService.remove(+id); } }
📌 NestJS generates the full CRUD boilerplate: nest generate resource users creates controller, service, DTOs, and module in one command.