Skip to main content
This quickstart guide will walk you through creating a simple REST API with tsoa, from installation to testing your first endpoint.

Prerequisites

  • Node.js 18.0.0 or higher
  • npm, yarn, or pnpm
  • Basic TypeScript knowledge

Step 1: Initialize Your Project

1

Create a new project

mkdir my-tsoa-api
cd my-tsoa-api
npm init -y
2

Install dependencies

npm install tsoa express
npm install -D typescript @types/node @types/express
3

Initialize TypeScript

Create a tsconfig.json file:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2021",
    "module": "commonjs",
    "lib": ["ES2021"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "node",
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
The experimentalDecorators and emitDecoratorMetadata options are required for tsoa to work properly.
4

Configure tsoa

Create a tsoa.json configuration file:
tsoa.json
{
  "entryFile": "src/server.ts",
  "noImplicitAdditionalProperties": "throw-on-extras",
  "spec": {
    "outputDirectory": "dist",
    "specVersion": 3
  },
  "routes": {
    "routesDir": "src"
  }
}

Step 2: Create Your First Controller

Create a simple user controller with CRUD operations:
src/controllers/userController.ts
import { Body, Controller, Get, Path, Post, Route, Tags } from 'tsoa';

// Define your data model
interface User {
  id: number;
  name: string;
  email: string;
}

// In-memory storage for demo purposes
const users: User[] = [
  { id: 1, name: 'John Doe', email: 'john@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];

@Route('users')
@Tags('Users')
export class UserController extends Controller {
  /**
   * Retrieves all users
   * @summary Get all users
   */
  @Get()
  public async getUsers(): Promise<User[]> {
    return users;
  }

  /**
   * Retrieves a specific user by ID
   * @summary Get user by ID
   * @param userId The user's identifier
   */
  @Get('{userId}')
  public async getUser(@Path() userId: number): Promise<User> {
    const user = users.find(u => u.id === userId);
    if (!user) {
      this.setStatus(404);
      throw new Error('User not found');
    }
    return user;
  }

  /**
   * Creates a new user
   * @summary Create a new user
   */
  @Post()
  public async createUser(
    @Body() body: { name: string; email: string }
  ): Promise<User> {
    const newUser: User = {
      id: users.length + 1,
      name: body.name,
      email: body.email
    };
    users.push(newUser);
    this.setStatus(201);
    return newUser;
  }
}
JSDoc comments like @summary are automatically included in your OpenAPI specification for better documentation.

Step 3: Generate Routes and Spec

Add these scripts to your package.json:
package.json
{
  "scripts": {
    "build": "tsoa spec-and-routes && tsc",
    "dev": "npm run build && node dist/server.js"
  }
}
Run the build command:
npm run build
This will generate:
  • dist/swagger.json - Your OpenAPI specification
  • src/routes.ts - Express route handlers with validation

Step 4: Create Your Server

Create the Express server that uses the generated routes:
src/server.ts
import express, { Application } from 'express';
import { RegisterRoutes } from './routes';

const app: Application = express();
const PORT = 3000;

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Register tsoa routes
RegisterRoutes(app);

// Error handling
app.use((err: any, req: any, res: any, next: any) => {
  const status = err.status || 500;
  const message = err.message || 'Internal Server Error';
  res.status(status).json({ message });
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
  console.log(`OpenAPI spec available at dist/swagger.json`);
});

Step 5: Run Your API

Start the server:
npm run dev
Your API is now running on http://localhost:3000!

Step 6: Test Your Endpoints

Try these curl commands to test your API:
curl http://localhost:3000/users

What Just Happened?

Let’s break down what tsoa did for you:
  1. Analyzed your TypeScript code - tsoa read your controller decorators and type definitions
  2. Generated OpenAPI spec - Created a complete swagger.json with all your endpoints, parameters, and response types
  3. Generated route handlers - Created routes.ts with:
    • Express route registration
    • Request validation based on your TypeScript types
    • Automatic serialization/deserialization
    • Error handling

View Your OpenAPI Spec

Your generated OpenAPI specification is available at dist/swagger.json. You can:
# View the spec
cat dist/swagger.json

Next Steps

Core Concepts

Learn about controllers, decorators, and models

Authentication

Add authentication and authorization to your API

Validation

Understand tsoa’s validation capabilities

Framework Guides

Detailed setup guides for Express, Koa, and Hapi

Troubleshooting

Make sure your tsoa.json is in the project root and the entryFile path is correct. Also ensure your TypeScript compilation is successful.
Verify that experimentalDecorators and emitDecoratorMetadata are enabled in your tsconfig.json.
Check that your request body matches the expected TypeScript types. tsoa validates all incoming requests based on your type definitions.
Make sure you’ve called RegisterRoutes(app) in your server file and that your routes are being generated in the correct directory specified in tsoa.json.

Build docs developers (and LLMs) love