# Middlewares

is similar to the Express middleware with the difference that it's a class and you can use the IoC to inject other services on its constructor.

All middlewares decorated by have one method named use(). This method can use all parameters decorators as you could see with the Controllers and return a promise.

# Configuration

To begin, you must add the middlewares folder on the componentsScan attribute in your server settings as follow :

import {Configuration} from "@tsed/common";

const rootDir = __dirname;

@Configuration({
  rootDir,
  mount: {
    "/rest": `${rootDir}/controllers/**/**.ts`
  },
  componentsScan: [
    `${rootDir}/services/**/**.ts`,
    `${rootDir}/middlewares/**/**.ts`
  ]
})
export class Server {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Then, create a new file in your middlewares folder. Create a new Class definition then add the decorator.

import {Middleware, Req} from "@tsed/common";

@Middleware()
export class CustomMiddleware {
  use(@Req() req: Req) {
    console.log("ID", req.id);
  }
}
1
2
3
4
5
6
7
8

You have different usecases to declare and use a middleware as following:

Note

Global middleware and endpoint middleware are similar, except that the Endpoint middleware can access to the last executed endpoint information.

# Global middleware

Global middlewares are generally used to handle requests before or after controllers. For example the GlobalAcceptMimesMiddleware is used to check the mime type set in the request headers and throw an error when the mime don't match with server configuration.

import {Constant, IMiddleware, Middleware, Req} from "@tsed/common";
import {NotAcceptable} from "@tsed/exceptions";

@Middleware()
export default class GlobalAcceptMimesMiddleware implements IMiddleware {
  @Constant("acceptMimes")
  acceptMimes: string[];

  use(@Req() request: Req) {
    if (!request.accepts(this.acceptMimes)) {
      throw new NotAcceptable("Accepted mimes are: " + this.acceptMimes.join(", "));
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Then add your middleware on the Server by using the right hook:

import {Configuration, Inject, PlatformApplication} from "@tsed/common";
import {GlobalAcceptMimeMiddleware} from "./GlobalAcceptMimeMiddleware";

const rootDir = __dirname;

@Configuration({
  rootDir,
  componentsScan: [
    `${rootDir}/middlewares/**/**.js`
  ],
  acceptMimes: ["application/json"]  // add your custom configuration here
})
export class Server {
  @Inject()
  app: PlatformApplication;

  $beforeRoutesInits() {
    this.app.use(GlobalAcceptMimeMiddleware);
  }

  // or
  $afterRoutesInit() {
    this.app.use(GlobalAcceptMimeMiddleware); // But maybe is too late ;)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# Endpoint middleware

Endpoint middleware is not really different from global middleware, but its goal is to handle a request before or after endpoint. It knows which endpoint is executed by using the decorator.

The following example, show you how to implement the middleware and use it with a custom decorator.

    Middleware can be used on a class controller or endpoint method with the following decorators:

    • or routes decorators: , , , and
    import {Controller, Get, UseBefore} from "@tsed/common";
    import {CustomMiddleware} from "./middlewares/CustomMiddleware";
    
    @Controller("/test")
    @UseBefore(CustomMiddleware) // global to the controller
    class MyCtrl {
      @Get("/")
      @UseBefore(CustomMiddleware) // only to this endpoint
      getContent() {
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # Error middleware

    Express allows you to handle any error when your middleware have 4 parameters like this:

    function (error, req, res, next){}
    
    1

    Ts.ED has the same mechanism with decorator. Use this decorator on a middleware to create a handler which will only called when an error occurs on th decorated endpoint.

    import {Err, Middleware, Next} from "@tsed/common";
    
    @Middleware()
    export class MyMiddlewareError {
      use(@Err() err: unknown, @Next() next: Next) {
        console.log('===> Error:', err);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    The following example is the GlobalErrorHandlerMiddleware used by Ts.ED to handle all errors thrown by your application.

    If you planed to catch errors globally see our Exception filter page.

    # Specifics parameters decorators

    In addition, you have these specifics parameters decorators for the middlewares:

    Signature Description
    Inject the Express.Err service.
    Provide the data returned by the previous middlewares.
    Provide the endpoint information.

    # Call sequences

    As you see in the previous section, a middleware can be used on different contexts:

    A middleware added to a controller or endpoint level has the same constraint as the endpoint method itself. It'll be played only when the url request matches with the path associated to the controller and its endpoint method.

    When a request is sent to the server all middlewares added in the Server, Controller or Endpoint with decorators will be called while a response isn't sent by one of the handlers/middlewares in the stack.

    Note

    send a response only when data is returned by the endpoint method or if the endpoint is the latest called endpoint for the resolved route.

    TIP

    The middlewares shown in the Endpoints box will be replayed as many times as it has endpoint that matches the request url.

    WARNING

    Only middlewares shown in the Endpoints box can use decorator to retrieve endpoint context execution.

    For example:

    import {Controller, Get, Next, Use, UseAfter, UseBefore, UseBeforeEach} from "@tsed/common";
    
    @Controller("/")
    @UseAfter(MdlwCtrlAfter)
    @UseBefore(MdlwCtrlBefore)
    @UseBeforeEach(MdlwCtrlBeforeEach)
    @Use(MdlwCtrl)
    export class MyCtrl {
    
      @Get("/")
      @UseBefore(MdlwBefore)
      @Use(Mdlw)
      @UseAfter(MdlwAfter)
      endpointA(@Next() next: Next) {
        console.log("EndpointA");
        next();
      }
    
      @Get("/")
      endpointB() {
        console.log("EndpointB");
    
        return {};
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    According to the call sequence scheme, the stack calls will be there:

    • Middlewares added in Server (logger, express middleware, etc...),
    • MdlwCtrlBefore,
    • MdlwCtrlBeforeEach
    • MdlwBefore,
    • MdlwCtrl,
    • MyCtrl.endpointA,
    • MdlwAfter,
    • SendResponse, (but no data is returned by the endpointA)
    • MdlwCtrlBeforeEach
    • MdlwCtrl,
    • MyCtrl.endpointB,
    • MdlwAfter,
    • SendResponse, send a response because endpointB returns data,
    • MdlwCtrlAfter, but this middleware will not be called because a response is sent.
    • Middleware added in Server (not called too).

    # Override existing middlewares

    The decorator gives you the ability to override some internal Ts.ED middlewares.

    import {Context, OriginalMiddleware, OverrideProvider} from "@tsed/common";
    
    @OverrideProvider(OriginalMiddleware)
    export class CustomMiddleware extends OriginalMiddleware {
      public use(@Context() ctx: Context) {
        ctx.response; // Ts.ED response
        ctx.request; // Ts.ED resquest
        ctx.getResponse(); // return Express.js or Koa.js response
        ctx.getRequest(); // return Express.js or Koa.js request
    
        // Do something
        return super.use();
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    Here we use the new Platform API to write our middleware. By using decorator and class we can get some information:

    • The data returned by the last executed endpoint,
    • The itself,
    • The and classes abstraction. These classes allow better code abstraction by exposing methods that are agnostic to Express.js.

    WARNING

    Ts.ED have is own . Override this middleware is not necessary, since you can create you own middleware and register it in your server before the original GlobalErrorHandlerMiddleware. For more details, see our Exceptions documentation page.

    TIP

    By default, the server imports automatically your middlewares matching with this rules ${rootDir}/middlewares/**/*.ts (See componentScan configuration).

    .
    ├── src
    │   ├── controllers
    │   ├── services
    │   ├── middlewares
    │   └── Server.ts
    └── package.json
    
    1
    2
    3
    4
    5
    6
    7

    If not, just import your middleware in your server or edit the componentScan configuration.

    import {Configuration} from "@tsed/common";
    import "./src/other/directory/CustomMiddleware";
    
    @Configuration({
        ...
    })
    export class Server {
     
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # Provided middlewares