# tsoa: Generating OpenAPI Specs and Express, Koa or Hapi Routes from TypeScript Controllers

> tsoa treats TypeScript controllers and models as the single source of truth, emitting a valid OpenAPI 2.0 or 3.0 document plus the route bindings for Express, Hapi or Koa. It suits teams that already think in TypeScript types and want validation and documentation to fall out of the same annotations.

**lukeautry/tsoa** — Build OpenAPI-compliant REST APIs using TypeScript and Node

- Repository: https://github.com/lukeautry/tsoa
- Stars: 3,974 · Forks: 533
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/lukeautry-tsoa

## The duplication tsoa removes between TypeScript types and an OpenAPI document

A typical Node API keeps the same shape in three places: the TypeScript interface the handler receives, the JSON Schema or OpenAPI definition published to consumers, and the runtime validation that rejects a bad body before the handler sees it. Those three drift. Someone adds an optional field to the interface, the spec is not regenerated, and the documented contract is now wrong in a way no compiler will catch.

tsoa's stated goal is to make TypeScript controllers and models the single source of truth. The generated OpenAPI document derives paths such as GET /users from the controller methods, definitions from TypeScript interfaces, and required or optional status from the type system itself: the README gives the example that myProperty?: string becomes optional in the OpenAPI spec. jsDoc supplies the free-text descriptions that types cannot express. The audience is a team already committed to TypeScript on Node that wants the spec to be an artifact of the code rather than a document maintained next to it.

## How controllers, models and the generator pipeline fit together

The mechanism is code generation in two directions from one set of annotations. You write controllers whose methods are decorated to declare the HTTP verb and path, and models as interfaces or classes. A generation step reads those and writes out two things: an OpenAPI 2.0 or 3.0 document, and route files for the middleware you chose.

The README lists Express, Hapi and Koa as supported middleware and notes that other middleware can be supported through a handlebars template. That template detail matters more than it looks: route generation is not a hardcoded Express emitter, it is templating over a model of your routes, so a framework outside the three is a template away rather than a fork away. The generated routes also validate request payloads, which is where the third copy of the shape disappears.

The project is explicit that validation and schema can diverge, and that the generator logs warnings when they do. The README states that enabling OpenAPI 3 minimizes the chances of divergent validation logic because OpenAPI 3 has a more expressive schema syntax. That is a real design constraint, not a footnote: if you target 2.0, some of what your TypeScript expresses has no faithful schema representation, and tsoa tells you so in warnings rather than silently guessing.

## Installing tsoa and generating a first route

The README does not list install commands inline. It points to the documentation site, the API reference and a getting started guide, so the canonical setup steps live at tsoa-community.github.io/docs/getting-started. The package itself is published to npm as tsoa, and the monorepo root declares engines of node >=18.0.0 and yarn >=1.9.4, so a Node 18 or newer runtime is the floor.

The README directs readers to example controllers in the tests directory at tests/fixtures/controllers and example models in tests/fixtures/testModel.ts. Those files are the reference for how annotations are written, and they are the right place to look before writing your own controller, since the README itself does not reproduce a controller snippet.

The repository layout shows the packages directory alongside tests and tests/esm, with the root package.json running lerna across the workspace. That is the structure a contributor works in rather than the structure a consumer installs.

## Where tsoa stops and your framework begins

tsoa generates routes and validates payloads. It does not give you a dependency injection container, a module system, configuration loading, or an application lifecycle. Those are not omissions to be fixed later; they are the boundary of the tool. If your handlers need services injected, you wire that yourself in whatever way your Express, Hapi or Koa setup already does.

The practical failure mode is a mismatch between what TypeScript permits and what the generated schema can express. The README acknowledges this directly and says differences in validation logic are clarified by logging warnings during generation of the OpenAPI spec or the routes. Warnings are easy to ignore in a build log. A team that treats them as noise can ship an endpoint whose documented contract and runtime validation disagree, which is exactly the problem tsoa exists to remove.

The other case where tsoa is the wrong tool is a project that is not TypeScript-first. The whole value proposition rests on type annotations carrying metadata. A JavaScript codebase would end up expressing everything through decorators and jsDoc, at which point a schema-first generator is a better fit.

## tsoa compared with NestJS on the OpenAPI question

NestJS is the comparison people reach for, and the difference is architectural rather than cosmetic. NestJS is an application framework: it owns modules, providers, dependency injection, the HTTP adapter and the request pipeline, and its Swagger integration decorates that existing structure to emit an OpenAPI document.

tsoa inverts the emphasis. It is a generator that produces routes for a middleware you already chose, and it treats the TypeScript type system rather than a decorator DSL as the primary source of schema metadata, falling back to decorators only where annotations cannot express the intent, and to jsDoc for pure text. If you want the framework to make more decisions for you, NestJS does that. If you want to keep your existing Express, Hapi or Koa application and derive the spec and validation from types, tsoa is the narrower and more direct fit. The README's philosophy section states the ordering plainly: rely on type annotations where possible, use decorators when they are not an appropriate way to express metadata, and use jsDoc for text.

## Maintenance, licensing and the cost of upgrading

tsoa is MIT licensed, which permits commercial and closed-source use; that is a statement about the licence text, not legal advice, and anyone with compliance obligations should read the LICENSE file in the repository root.

The repository is not archived, and the last push was on 2026-09-23. Recent releases tell a more layered story. v6.6.0 was published on 2024-12-08 and v6.5.0 on 2024-10-14, while v7.0.0-alpha.0 appeared on 2025-12-14. The stable line and the next major line are therefore moving at different speeds, and a team that wants the newest schema handling is looking at a prerelease, which the version string makes explicit.

The README also carries an open call for maintainers, pointing to a GitHub issue for anyone willing to take on the role, and notes the volume of pull requests and issues. That is a maintenance signal worth reading before you standardise on the project: the code is being pushed to, and the project is asking for more hands on it.

Upgrade cost concentrates in the generator, because the generator is where your annotations become a schema. A major version bump can change what your existing controllers emit, and the warnings logged during generation are the cheapest way to see that before runtime. The monorepo root pins typescript through resolutions and requires Node 18 or newer, so a TypeScript upgrade path is part of the upgrade story.

## Conclusion

Adopt tsoa if your API is already defined by TypeScript interfaces and you want the OpenAPI document and the Express, Hapi or Koa route bindings produced from those same declarations rather than hand-maintained alongside them. Skip it if you need a framework that owns dependency injection, module wiring and the HTTP layer end to end, since tsoa stops at generation and validation and leaves the server framework to you. Before committing, check which OpenAPI version you can target, because the README states that enabling OpenAPI 3 reduces the chance of validation logic diverging from the generated schema, and read the warnings the generator logs during route generation to see what your model shapes will produce.

## FAQ

### What is tsoa used for?

tsoa builds OpenAPI-compliant REST APIs from TypeScript controllers and models, generating a valid OpenAPI 2.0 or 3.0 document plus routes for Express, Hapi or Koa. It also validates request payloads against the same types that produced the spec.

### Which middleware can tsoa generate routes for?

The README lists Express, Hapi and Koa as currently supported, and states that other middleware can be supported using a handlebars template. Route generation is templated rather than tied to a single framework.

### Does tsoa validate request bodies at runtime?

Yes. The README lists validating request payloads among the generated routes' responsibilities, and says runtime validation should behave as closely as possible to the generated OpenAPI schema. It also warns that differences in validation logic are logged as warnings during generation.

### What Node version does tsoa require?

The monorepo package.json declares node >=18.0.0 under engines, with engineStrict set to true. It also requires yarn >=1.9.4 for working in the repository itself.

### Should I target OpenAPI 2.0 or 3.0 with tsoa?

The README states that enabling OpenAPI 3 minimizes the chances of divergent validation logic, because OpenAPI 3 has a more expressive schema syntax. Targeting 2.0 is supported, but expect the generator to log warnings where your types cannot be represented faithfully.

## Sources

- [Issues](https://github.com/lukeautry/tsoa/issues)
- [License: MIT](https://github.com/lukeautry/tsoa/blob/master/LICENSE)
- [lukeautry/tsoa on GitHub](https://github.com/lukeautry/tsoa)
- [README](https://github.com/lukeautry/tsoa/blob/master/README.md)
- [Releases](https://github.com/lukeautry/tsoa/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lukeautry-tsoa
