Documentation Index
Fetch the complete documentation index at: https://mintlify.com/lukeautry/tsoa/llms.txt
Use this file to discover all available pages before exploring further.
This guide walks you through integrating tsoa with an Express.js application, from initial setup to running your API server.
Installation
Install Dependencies
Install tsoa along with Express and its TypeScript definitions:npm install tsoa express
npm install --save-dev @types/express @types/node typescript
Configure tsoa
Create a tsoa.json configuration file in your project root:{
"entryFile": "src/app.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"spec": {
"outputDirectory": "build",
"specVersion": 3
},
"routes": {
"routesDir": "src",
"middleware": "express"
}
}
The middleware field must be set to "express" for Express.js projects.
Create a Controller
Create your first controller with tsoa decorators:src/controllers/userController.ts
import { Controller, Get, Post, Route, Body, Path, SuccessResponse } from 'tsoa';
interface User {
id: number;
name: string;
email: string;
}
interface CreateUserRequest {
name: string;
email: string;
}
@Route('users')
export class UserController extends Controller {
/**
* Retrieves a user by ID
* @param userId The user's identifier
*/
@Get('{userId}')
public async getUser(@Path() userId: number): Promise<User> {
return {
id: userId,
name: 'John Doe',
email: 'john@example.com'
};
}
/**
* Creates a new user
*/
@Post()
@SuccessResponse('201', 'Created')
public async createUser(@Body() requestBody: CreateUserRequest): Promise<User> {
this.setStatus(201);
return {
id: Math.floor(Math.random() * 10000),
...requestBody
};
}
}
Set Up Express Server
Create your Express application and register tsoa routes:import express, { Request, Response, NextFunction } from 'express';
import bodyParser from 'body-parser';
import { RegisterRoutes } from './routes';
export const app = express();
// Middleware
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// Register tsoa routes
RegisterRoutes(app);
// Error handling - must come after route registration
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
const status = err.status || 500;
const body: any = {
fields: err.fields || undefined,
message: err.message || 'An error occurred during the request.',
name: err.name,
status,
};
res.status(status).json(body);
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
Generate Routes and Spec
Add scripts to your package.json:{
"scripts": {
"build": "tsoa spec-and-routes && tsc",
"start": "node build/app.js",
"dev": "tsoa spec-and-routes && ts-node src/app.ts"
}
}
Generate routes and OpenAPI spec:This creates:
src/routes.ts - Generated route handlers
build/swagger.json - OpenAPI specification
Start the Server
Your API is now running at http://localhost:3000!Test it:curl http://localhost:3000/users/1
Router vs Application
tsoa supports both Express Application and Router instances:
import express from 'express';
import { RegisterRoutes } from './routes';
const app = express();
RegisterRoutes(app);
Request Context
Access the raw Express request and response objects in your controllers:
import { Controller, Get, Request, Response } from 'tsoa';
import { Request as ExpressRequest, Response as ExpressResponse } from 'express';
@Route('example')
export class ExampleController extends Controller {
@Get('with-context')
public async withContext(
@Request() request: ExpressRequest
): Promise<void> {
// Access request headers
const userAgent = request.headers['user-agent'];
// Access custom middleware values
const customValue = (request as any).customValue;
return { userAgent, customValue };
}
}
Middleware Integration
Apply Express middleware to specific routes or controllers:
import { Controller, Get, Middlewares } from 'tsoa';
import { Request, Response, NextFunction } from 'express';
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
// Your auth logic
if (req.headers.authorization) {
next();
} else {
res.status(401).json({ message: 'Unauthorized' });
}
};
const loggingMiddleware = (req: Request, res: Response, next: NextFunction) => {
console.log(`${req.method} ${req.path}`);
next();
};
@Route('protected')
@Middlewares(authMiddleware)
export class ProtectedController extends Controller {
@Get('data')
@Middlewares(loggingMiddleware)
public async getData(): Promise<any> {
return { data: 'sensitive information' };
}
}
Custom Base Path
Configure a base path for all routes:
{
"routes": {
"routesDir": "src",
"middleware": "express",
"basePath": "/api/v1"
}
}
All routes will be prefixed with /api/v1.
Advanced Configuration
Set custom response headers:
@Get('with-headers')
public async withHeaders(): Promise<User> {
this.setHeader('X-Custom-Header', 'value');
this.setHeader('Cache-Control', 'no-cache');
return user;
}
Multiple Status Codes
Document multiple possible status codes:
import { Controller, Get, Response } from 'tsoa';
interface ErrorResponse {
message: string;
}
@Route('items')
export class ItemController extends Controller {
@Get('{id}')
@Response<ErrorResponse>(404, 'Not Found')
@Response<ErrorResponse>(500, 'Internal Server Error')
public async getItem(@Path() id: number): Promise<Item> {
const item = await findItem(id);
if (!item) {
this.setStatus(404);
return { message: 'Item not found' } as any;
}
return item;
}
}
Common Patterns
Query Parameters
import { Controller, Get, Query } from 'tsoa';
@Route('search')
export class SearchController extends Controller {
@Get()
public async search(
@Query() q: string,
@Query() limit?: number,
@Query() offset?: number
): Promise<SearchResult[]> {
return performSearch(q, limit || 10, offset || 0);
}
}
import { Controller, Get, Header } from 'tsoa';
@Route('api')
export class ApiController extends Controller {
@Get('version')
public async getVersion(
@Header('x-api-version') apiVersion?: string
): Promise<any> {
return { requestedVersion: apiVersion };
}
}
Next Steps
File Uploads
Learn how to handle file uploads with multer
Authentication
Secure your API with authentication
Validation
Implement request validation
Dependency Injection
Use IoC containers with your controllers