Skip to main content

Overview

The Config interface defines the top-level configuration for tsoa. It combines OpenAPI specification generation settings with route generation settings, along with global options that apply to both.

Configuration File

Create a tsoa.json file in your project root:
{
  "entryFile": "src/app.ts",
  "noImplicitAdditionalProperties": "throw-on-extras",
  "controllerPathGlobs": ["src/controllers/**/*.ts"],
  "spec": {
    "outputDirectory": "dist",
    "specVersion": 3
  },
  "routes": {
    "routesDir": "src",
    "middleware": "express"
  }
}

Properties

entryFile

entryFile
string
required
The entry point to your API application.This file should import or reference all of your controllers, either directly or indirectly.
{
  "entryFile": "src/server.ts"
}

spec

spec
SpecConfig
required
OpenAPI specification generation configuration.See SpecConfig for detailed options.
{
  "spec": {
    "outputDirectory": "dist",
    "specVersion": 3,
    "name": "My API",
    "version": "1.0.0"
  }
}

routes

routes
RoutesConfig
required
Route generation configuration.See RoutesConfig for detailed options.
{
  "routes": {
    "routesDir": "src",
    "middleware": "express",
    "basePath": "/api"
  }
}

controllerPathGlobs

controllerPathGlobs
string[]
Array of glob patterns pointing to your controller files.If not specified, tsoa will scan files imported from entryFile.
{
  "controllerPathGlobs": [
    "src/controllers/**/*.ts",
    "src/modules/**/controllers/*.ts"
  ]
}

ignore

ignore
string[]
Directories to ignore during TypeScript metadata scan.Useful for excluding test files, build output, and node_modules.
{
  "ignore": [
    "**/node_modules/**",
    "**/dist/**",
    "**/*.test.ts",
    "**/*.spec.ts"
  ]
}

noImplicitAdditionalProperties

noImplicitAdditionalProperties
'throw-on-extras' | 'silently-remove-extras' | 'ignore'
Controls how additional properties in request bodies are handled.
  • 'throw-on-extras': Reject requests with extra properties (recommended)
  • 'silently-remove-extras': Remove extra properties without error
  • 'ignore': Allow extra properties (not recommended for security)
{
  "noImplicitAdditionalProperties": "throw-on-extras"
}
Example behavior:
interface CreateUserRequest {
  username: string;
  email: string;
}

@Post('users')
public async createUser(@Body() body: CreateUserRequest): Promise<User> {
  return await userService.create(body);
}

// Request body:
{
  "username": "john",
  "email": "[email protected]",
  "isAdmin": true  // Extra property
}

// With 'throw-on-extras': Returns 422 validation error
// With 'silently-remove-extras': Creates user without isAdmin
// With 'ignore': Creates user, isAdmin is available in body

compilerOptions

compilerOptions
Record<string, unknown>
TypeScript compiler options to use during generation.Overrides options from tsconfig.json.
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strictNullChecks": true
  }
}

defaultNumberType

defaultNumberType
'double' | 'float' | 'integer' | 'long'
default:"double"
Default OpenAPI number type for TypeScript’s number type when no type annotation is present.
{
  "defaultNumberType": "integer"
}
Usage:
// Without annotation
@Get('count')
public async getCount(): Promise<number> {
  // OpenAPI type: integer (if defaultNumberType is 'integer')
  return 42;
}

// With annotation
/**
 * @isFloat
 */
@Get('average')
public async getAverage(): Promise<number> {
  // OpenAPI type: float (annotation overrides default)
  return 42.5;
}

multerOpts

multerOpts
MulterOpts
deprecated
Multer options for file upload handling.Deprecated: Since v6.4.0. Pass multer options directly to RegisterRoutes instead.
{
  "multerOpts": {
    "dest": "/tmp/uploads"
  }
}
Modern approach (recommended):
import express from 'express';
import multer from 'multer';
import { RegisterRoutes } from './routes';

const app = express();

const multerOpts = {
  dest: '/tmp/uploads',
  limits: {
    fileSize: 10 * 1024 * 1024 // 10MB
  }
};

// Pass multer options to RegisterRoutes
RegisterRoutes(app, { multerOpts });

Complete Example

Minimal Configuration

{
  "entryFile": "src/app.ts",
  "spec": {
    "outputDirectory": "dist",
    "specVersion": 3
  },
  "routes": {
    "routesDir": "src",
    "middleware": "express"
  }
}

Production Configuration

{
  "entryFile": "src/server.ts",
  "controllerPathGlobs": [
    "src/controllers/**/*.ts",
    "src/modules/**/controllers/*.ts"
  ],
  "ignore": [
    "**/node_modules/**",
    "**/dist/**",
    "**/*.test.ts",
    "**/*.spec.ts",
    "**/mocks/**"
  ],
  "noImplicitAdditionalProperties": "throw-on-extras",
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "defaultNumberType": "double",
  "spec": {
    "outputDirectory": "public/api-docs",
    "specFileBaseName": "openapi",
    "specVersion": 3,
    "yaml": true,
    "name": "E-Commerce API",
    "version": "2.0.0",
    "description": "RESTful API for e-commerce platform",
    "license": "MIT",
    "contact": {
      "name": "API Support",
      "email": "[email protected]",
      "url": "https://example.com/support"
    },
    "host": "api.example.com",
    "basePath": "/v2",
    "schemes": ["https"],
    "securityDefinitions": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "api_key": {
        "type": "apiKey",
        "name": "X-API-Key",
        "in": "header"
      }
    },
    "rootSecurity": [
      { "bearer": [] }
    ],
    "tags": [
      {
        "name": "Users",
        "description": "User management operations"
      },
      {
        "name": "Products",
        "description": "Product catalog management"
      },
      {
        "name": "Orders",
        "description": "Order processing and management"
      }
    ]
  },
  "routes": {
    "routesDir": "src/generated",
    "routesFileName": "routes.ts",
    "middleware": "express",
    "basePath": "/api/v2",
    "authenticationModule": "./src/authentication",
    "iocModule": "./src/inversify.config",
    "esm": false,
    "noWriteIfUnchanged": true,
    "bodyCoercion": true
  }
}

Monorepo Configuration

{
  "entryFile": "packages/api/src/server.ts",
  "controllerPathGlobs": [
    "packages/api/src/controllers/**/*.ts",
    "packages/shared/src/controllers/**/*.ts"
  ],
  "ignore": [
    "**/node_modules/**",
    "**/dist/**",
    "**/build/**",
    "**/*.test.ts"
  ],
  "noImplicitAdditionalProperties": "throw-on-extras",
  "spec": {
    "outputDirectory": "packages/api/dist",
    "specVersion": 3,
    "name": "Monorepo API",
    "version": "1.0.0"
  },
  "routes": {
    "routesDir": "packages/api/src/generated",
    "middleware": "express",
    "basePath": "/api"
  }
}

TypeScript Configuration

You can also define configuration in TypeScript:

tsoa.config.ts

import { Config } from 'tsoa';

const config: Config = {
  entryFile: './src/server.ts',
  controllerPathGlobs: ['./src/controllers/**/*.ts'],
  noImplicitAdditionalProperties: 'throw-on-extras',
  
  spec: {
    outputDirectory: './dist',
    specVersion: 3,
    name: 'My API',
    version: process.env.API_VERSION || '1.0.0',
    description: 'API Documentation',
    host: process.env.API_HOST || 'localhost:3000',
    basePath: '/api',
    schemes: process.env.NODE_ENV === 'production' ? ['https'] : ['http'],
    securityDefinitions: {
      bearer: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT'
      }
    }
  },
  
  routes: {
    routesDir: './src',
    middleware: 'express',
    basePath: '/api',
    authenticationModule: './src/authentication'
  }
};

export const specConfig = config.spec;
export const routesConfig = config.routes;
export default config;

Usage

import { generateSpec, generateRoutes } from 'tsoa';
import config from './tsoa.config';

(async () => {
  const metadata = await generateSpec(config.spec);
  await generateRoutes(config.routes, undefined, undefined, metadata);
})();

Loading Configuration

The tsoa CLI automatically loads configuration from:
  1. Command line argument: -c ./custom-config.json
  2. tsoa.json in the current directory
  3. tsoa field in package.json

package.json

{
  "name": "my-api",
  "version": "1.0.0",
  "tsoa": {
    "entryFile": "src/app.ts",
    "spec": {
      "outputDirectory": "dist",
      "specVersion": 3
    },
    "routes": {
      "routesDir": "src",
      "middleware": "express"
    }
  }
}

Environment-Specific Configuration

// config/tsoa.development.ts
import { Config } from 'tsoa';

const config: Config = {
  entryFile: './src/server.ts',
  spec: {
    outputDirectory: './dist',
    specVersion: 3,
    host: 'localhost:3000',
    schemes: ['http']
  },
  routes: {
    routesDir: './src',
    middleware: 'express'
  }
};

export default config;

// config/tsoa.production.ts
import { Config } from 'tsoa';

const config: Config = {
  entryFile: './src/server.ts',
  spec: {
    outputDirectory: './dist',
    specVersion: 3,
    host: 'api.example.com',
    schemes: ['https']
  },
  routes: {
    routesDir: './src',
    middleware: 'express'
  }
};

export default config;

// Load based on environment
import devConfig from './config/tsoa.development';
import prodConfig from './config/tsoa.production';

const config = process.env.NODE_ENV === 'production' ? prodConfig : devConfig;

See Also

Build docs developers (and LLMs) love