Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Controller
✓ import { Controller, Route, Get, Post, Body, Path, Query } from 'tsoa'
✗ import { Controller } from '@tsoa/runtime'
Most decorators and the base Controller class are imported directly from the 'tsoa' package. While `@tsoa/runtime` exists, `tsoa` is the primary entry point for decorators.
RegisterRoutes
✓ import { RegisterRoutes } from '../build/routes'
✗ import { RegisterRoutes } from 'tsoa'
RegisterRoutes is a function generated by the `tsoa routes` CLI command. Its path is relative to your compiled application entry file and depends on the `routesDir` setting in `tsoa.json`. Common mistake is to try and import it directly from 'tsoa' or use an incorrect relative path.
tsoa CLI
✓ npx tsoa spec && npx tsoa routes
✗ tsoa generate
tsoa's primary functions are accessed via CLI commands `tsoa spec` to generate OpenAPI definition and `tsoa routes` to generate route handlers. These commands are typically run as part of a build script.
This quickstart demonstrates setting up a basic tsoa-powered Express API, including defining a controller with decorators, registering generated routes, and serving the OpenAPI documentation using swagger-ui-express. It highlights the compile-time code generation steps necessary for tsoa applications.
// tsoa.json (in project root)
// {
// "entryFile": "src/app.ts",
// "controllers": ["src/controllers/**/*.ts"],
// "spec": {
// "outputDirectory": "build",
// "specVersion": 3,
// "yaml": false
// },
// "routes": {
// "routesDir": "build"
// }
// }
// src/controllers/usersController.ts
import { Controller, Route, Get, Path, Post, Body, SuccessResponse } from 'tsoa';
interface User {
id: number;
name: string;
}
interface CreateUserRequest {
name: string;
}
@Route('users')
export class UsersController extends Controller {
private users: User[] = [{ id: 1, name: 'Alice' }];
@Get('{userId}')
public async getUser(@Path() userId: number): Promise<User | undefined> {
return this.users.find(u => u.id === userId);
}
@SuccessResponse(201, 'Created') // Set HTTP status code for success
@Post()
public async createUser(@Body() requestBody: CreateUserRequest): Promise<User> {
const newUser: User = {
id: this.users.length + 1,
name: requestBody.name,
};
this.users.push(newUser);
return newUser;
}
}
// src/app.ts (your Express server entry file)
import express from 'express';
import { RegisterRoutes } from '../build/routes'; // Path relative to app.ts's compiled output
import * as swaggerUi from 'swagger-ui-express';
import * as path from 'path';
const app = express();
app.use(express.json()); // For parsing application/json
app.use(express.urlencoded({ extended: true })); // For parsing application/x-www-form-urlencoded
RegisterRoutes(app); // Register the tsoa-generated routes
// Serve OpenAPI UI
try {
// Ensure 'swagger.json' is generated by `tsoa spec` in your build directory
const swaggerDocument = require(path.resolve(__dirname, '../build/swagger.json'));
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
} catch (error) {
console.error('Failed to load swagger.json. Did you run `tsoa spec`?', error);
}
// Generic error handler middleware
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error(err);
const status = err.status || 500;
const message = err.message || 'An unexpected error occurred.';
res.status(status).json({ message });
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
console.log(`API docs available at http://localhost:${port}/docs`);
});
// To run this application:
// 1. Install dependencies: `npm install express tsoa swagger-ui-express @types/express @types/swagger-ui-express typescript ts-node @tsoa/runtime`
// 2. Configure `tsconfig.json` with: `"experimentalDecorators": true`, `"emitDecoratorMetadata": true` (recommended).
// 3. Add scripts to `package.json`: `"tsoa:gen": "tsoa spec && tsoa routes", "build": "npm run tsoa:gen && tsc", "start": "npm run build && node build/app.js"`
// 4. Run `npm start` to build and launch the server.
tsoa --version
Errors
Common errors & fixes
TS2307: Cannot find module '../build/routes' or its corresponding type declarations.
The `tsoa routes` command has not been executed, or the `routesDir` in `tsoa.json` is incorrect, preventing the generation of the `routes.ts` file.
fixRun `npx tsoa routes` as part of your build process. Verify that `routesDir` in `tsoa.json` matches the intended output directory, and that the import path in your `app.ts` is correct relative to its compiled location.
Error: No routes were found! Have you configured your tsoa.json correctly?
tsoa could not find any controller files matching the `controllerPathGlobs` specified in `tsoa.json`, or no classes within those files were decorated with `@Route()`.
fixCheck the `controllerPathGlobs` array in your `tsoa.json` to ensure it correctly points to your controller files (e.g., `"src/controllers/**/*.ts"`). Also, confirm that your controller classes have the `@Route()` decorator.
TypeError: Cannot read properties of undefined (reading 'body')
The Express (or other framework) application is not configured with middleware to parse incoming request bodies (e.g., JSON or URL-encoded data), or the wrong `@Body` decorator is used for the content type.
fixFor JSON payloads, ensure `app.use(express.json())` is called early in your Express application setup. For URL-encoded data, use `app.use(express.urlencoded({ extended: true }))`. Use appropriate tsoa decorators like `@Body()`, `@BodyProp()`, or `@FormField()` for different request body types. MulterError: Unexpected field
A mismatch exists between the field name used in the client's form data upload and the field name expected by the `@UploadedFile()` or `@UploadedFiles()` decorator in the controller, or Multer isn't correctly initialized/passed.
fixEnsure the string argument passed to `@UploadedFile('fieldName')` or `@UploadedFiles('fieldName')` exactly matches the `name` attribute of the file input in your HTML form or the key in your `FormData` object. If using custom Multer, ensure it's configured correctly. Error: target is a string value; tsconfig JSON must be parsed with parseJsonSourceFileConfigFileContent or getParsedCommandLineOfConfigFile before passing to createProgram
This error can occur in tsoa v6.5.0+ when `compilerOptions` are explicitly passed in `tsoa.json` directly as a string or in an incorrect format that TypeScript's `createProgram` cannot parse. This often happens with shared `tsconfig` files or non-standard configurations.
fixRemove the explicit `compilerOptions` field from `tsoa.json` and let tsoa infer them from your main `tsconfig.json`. If custom options are strictly needed, ensure they are provided in a correctly structured object that `ts.createProgram` expects, or consider passing a `compilerOptions` object programmatically to `generateSpec` and `generateRoutes` functions if using the API directly.
Upgrade
Version history
7.0.0-alpha.0latest on npm
Audit
Dependencies
typescriptrequiredRequired for compiling TypeScript controllers and generating metadata. It's a fundamental build-time dependency.
expressoptionalCommonly used web framework for which tsoa generates routes and expects runtime integration. Often installed alongside tsoa.
multeroptionalRequired for handling file uploads via @UploadedFile and @UploadedFiles decorators.
@tsoa/runtimerequiredRequired by generated routes file, especially important for pnpm users due to its symlinked node_modules structure.