# routing-controllers: decorator-based controllers for Express and Koa

> routing-controllers turns TypeScript classes into Express or Koa routes through decorators, with class-validator handling input. It suits teams already committed to TypeScript and a validation library, and it is a poor fit if you want zero decorator metadata or a single self-contained runtime.

**typestack/routing-controllers** — Create structured, declarative and beautifully organized class-based controllers with heavy decorators usage in Express / Koa using TypeScript and Routing Controllers Framework.

- Repository: https://github.com/typestack/routing-controllers
- Stars: 4,506 · Forks: 394
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/typestack-routing-controllers

## The problem routing-controllers solves for TypeScript servers

Express and Koa give you a request object and a response object. Route registration, parameter extraction, body parsing and error mapping are left to you, and in a TypeScript codebase that usually means a growing file of app.get and app.post calls with hand-written casts on req.body and req.params. routing-controllers moves that work into class methods. A method decorated with @Get('/users/:id') becomes a route, and the value it returns becomes the response body. The README states that the class registers routes specified in method decorators in your server framework, Express or Koa. The audience is teams that already run TypeScript on the server and want the routing layer to look like the rest of their code: classes, decorators, typed parameters. It is not a standalone HTTP server. It builds on Express or Koa and returns that framework's app instance, so anything you know about those frameworks still applies.

## How decorators become routes at startup

The mechanism depends on TypeScript emitting decorator metadata. The README requires emitDecoratorMetadata and experimentalDecorators in tsconfig.json, and requires reflect-metadata to be imported before routing-controllers is used. When you call createExpressServer with a controllers array, the library reads the decorator metadata attached to those classes and their methods, builds the route table, and registers it on a new Express app. The same call exists for Koa as createKoaServer. Parameter decorators such as @Param, @Body, @Query, @Header and @Cookie describe how each argument is filled from the request, and the README documents converting those parameters into class instances. That conversion is where class-transformer and class-validator enter: they are peer dependencies, not bundled, and the README notes they were direct dependencies in prior versions and are now peer dependencies so you choose when to upgrade and accept breaking changes. The library also supports middleware and interceptor hooks, global and per-controller, plus error handlers and an optional DI container.

## Installing routing-controllers and serving a first route

The README gives the install order. Install the library, then reflect-metadata, then your framework, then the peer dependencies. For Express the README lists express, body-parser and multer, with optional typings.

```bash
npm install routing-controllers
npm install reflect-metadata
npm install express body-parser multer
npm install -D @types/express @types/body-parser @types/multer
npm install class-transformer class-validator
```

The tsconfig.json file needs two compiler options, or the decorators will not carry the metadata the library reads.

```json
{
  "emitDecoratorMetadata": true,
  "experimentalDecorators": true
}
```

A controller is a class with method decorators. The README's UserController example returns plain strings, which become response bodies.

```typescript
import 'reflect-metadata';
import { Controller, Param, Body, Get, Post, Put, Delete } from 'routing-controllers';

@Controller()
export class UserController {
  @Get('/users')
  getAll() {
    return 'This action returns all users';
  }

  @Get('/users/:id')
  getOne(@Param('id') id: number) {
    return 'This action returns user #' + id;
  }
}
```

The entry file passes the controller to createExpressServer and listens on port 3000, matching the README example.

```typescript
import { createExpressServer } from 'routing-controllers';
import { UserController } from './UserController';

const app = createExpressServer({
  controllers: [UserController],
});

app.listen(3000);
```

Open http://localhost:3000/users and the README says you will see the string from getAll. Open /users/1 and you will see the string from getOne with the id interpolated. Koa users swap createKoaServer for createExpressServer; the README notes that is the only change at that call site.

## Where routing-controllers constrains your project

The dependency on decorator metadata is the main constraint. It ties the library to TypeScript compilation with two specific compiler flags, and to a reflect-metadata import that must run first. If your build strips decorators, or you compile with a toolchain that does not honor those flags, the route table will not be built. The peer dependency arrangement is the second constraint. Because class-transformer and class-validator sit outside the package, a major version bump in either one is your upgrade to schedule, and the README frames this as a deliberate trade so you control when you take breaking changes. That also means the library alone does not validate anything: if you never install class-validator, you get routing without validation. Finally, this is a router layer, not a runtime. It does not replace Express or Koa, so you inherit their middleware model, their error semantics and their performance profile. If your team prefers plain functions and explicit app.get calls, or you want one package that owns the whole request lifecycle, this is the wrong tool.

## How it differs from NestJS and from plain Express routers

NestJS is the closest widely used alternative, and the difference is scope. NestJS ships its own application context, module system, dependency injection container and CLI, and it owns the request lifecycle end to end. routing-controllers stays a library on top of Express or Koa: you call createExpressServer, you get an Express app back, and you keep using Express middleware, Express error handling and the Express ecosystem. There is an optional DI container integration, but it is not the organizing principle of the codebase. Against a plain Express Router, the difference is where the type information lives. With express.Router you write the path string and extract parameters manually, and the compiler does not check that the parameter names match. With routing-controllers, the decorator carries the path and the parameter decorators carry the extraction, so the declaration and the handler sit in one place. The cost is the build configuration and the peer dependencies described above. NestJS is the better choice when you want a framework opinion on modules and DI; routing-controllers is the better choice when you want to keep your existing Express or Koa application and only change how routes are declared.

## Maintenance, licence and upgrade cost

The repository is not archived. The last push was on 2026-05-23, which is within six months of today, and the most recent release listed is v0.11.3 from 2025-08-05, preceded by v0.11.2 in March 2025 and v0.11.1 in February 2025. The 0.x version number is the thing to plan around: minor releases can carry breaking changes, and the move of class-transformer and class-validator to peer dependencies is exactly that kind of change, described in the README as a shift from prior versions. Upgrading therefore means checking both the library changelog and the versions of your two peer validation packages. The licence is MIT, which permits commercial use and modification; the repository ships a LICENSE file at the root. This is a statement about the licence text, not legal advice, and if you redistribute the package or bundle it into a product you should read the licence yourself. The package.json declares sideEffects false and publishes CJS, ESM2015 and type builds, so bundlers can tree-shake it.

## Conclusion

Adopt routing-controllers if your server is already TypeScript, you accept experimentalDecorators and emitDecoratorMetadata in tsconfig.json, and you are willing to add class-validator and class-transformer as peer dependencies. Do not adopt it if you want a framework with no compiler flags, no metadata reflection and no separate validation package, or if you are on a runtime where reflect-metadata is not viable. Before committing, verify two things in your own project: that reflect-metadata is imported before any routing-controllers import, and that your Express or Koa version matches the peer package set the README lists for that framework.

## FAQ

### What is routing-controllers used for?

It creates controller classes whose decorated methods handle requests, and registers those routes on an Express or Koa application. The README describes it as allowing controller classes with methods as actions that handle requests.

### How do I install routing-controllers?

Install routing-controllers, then reflect-metadata, then your framework packages, then the peer dependencies class-transformer and class-validator. The README also requires emitDecoratorMetadata and experimentalDecorators in tsconfig.json and an import of reflect-metadata before use.

### Does routing-controllers work with Koa as well as Express?

Yes. The README states you can use routing-controllers with express.js or koa.js, and that Koa users call createKoaServer instead of createExpressServer. The Koa install set listed is koa, @koa/router, koa-bodyparser and @koa/multer.

### Are class-validator and class-transformer required with routing-controllers?

They are peer dependencies rather than bundled dependencies, and the README tells you to install them alongside the library. The README notes they were direct dependencies in prior versions and are now peer dependencies so you choose when to upgrade and accept breaking changes.

## Sources

- [Issues](https://github.com/typestack/routing-controllers/issues)
- [License: MIT](https://github.com/typestack/routing-controllers/blob/develop/LICENSE)
- [README](https://github.com/typestack/routing-controllers/blob/develop/README.md)
- [Releases](https://github.com/typestack/routing-controllers/releases)
- [typestack/routing-controllers on GitHub](https://github.com/typestack/routing-controllers)

---

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