ExpressoTS Middleware & Controller
Tags: typescript, expressots, tutorial, node
Published: 2024-03-06 08:10:36

[ExpressoTS Doc](https://doc.expresso-ts.com/docs/hello)

ExpressoTS as you may already know, is a lightweight but very robust backend typescript framework. As part of his continuous growth, the developer community has requested more flexibility in the way Middleware and Controller can be used in the application.
Guess what? We are delivering in the v2.8.0, the state of the art for handling, creating and configuring middleware and controllers with ExpressoTS.

## Middleware

Middleware is a function that has access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

ExpressoTS fully supports [Express](https://expressjs.com/) middleware.

## Adding Middleware

ExpressoTS application supports adding middleware globally to the application as well as per route. It offers all the [middleware supported by Express Team](https://expressjs.com/en/resources/middleware.html) out-of-the-box through the use of `this.middleware` property.

```typescript
@provide(App)
export class App extends AppExpress {
    private middleware: IMiddleware;

    constructor() {
        super();
        this.middleware = container.get<IMiddleware>(Middleware);
    }

    protected configureServices(): void {
        this.middleware.addBodyParser();
        this.middleware.addCors();
        this.middleware.addHelmet();
    }
}
```

If you add a middleware that is not installed as dependency, the application will throw a warning message and continue to run.

### Global Middleware

Middlewares can be added globally using the `App` class through the `this.middleware` property, using the out-of-the-box middleware list provided by Express Team:

```typescript
protected configureServices(): void {
        this.middleware.addBodyParser();
        this.middleware.addCors();
        this.middleware.addHelmet();
    }
```

For any other middleware, or a custom middleware, you can add it using the `this.middleware.addMiddleware()` method. Using the `addMiddleware` method, you can add any middleware from NPM Registry, custom Expressjs middleware or a custom ExpressoTS middleware.

NPM Registry middleware:

```typescript
this.middleware.addMiddleware(cors());
```

Custom Expressjs middleware:

```typescript
function myMiddleware(req: Request, res: Response, next: NextFunction) {
    // Do something
    next();
}
```

```typescript
this.middleware.addMiddleware(myMiddleware);
```

Custom ExpressoTS middleware:

```typescript
class CustomMiddleware extends ExpressoMiddleware {
    use(req: Request, res: Response, next: NextFunction): void | Promise<void> {
        // Do something
        next();
    }
}
```

```typescript
this.middleware.addMiddleware(new CustomMiddleware());
```

### Route Middleware

Middlewares can be added per route in the `App` class through the `this.middleware.addMiddleware()` method. You can add any middleware from NPM Registry, custom Expressjs middleware or a custom ExpressoTS middleware.

```typescript
this.middleware.addMiddleware({ path: "/api", middlewares: [] });
```

Each route can have multiple middlewares. Or you add a middleware to a specific route in the `Controller` class through the `@controller()` and/Or `http Method` decorators.

```typescript
@controller("/")
export class AppController {
    @Post("", express.json())
    execute() {
        return "Hello World";
    }
}
```

If you want to apply a middleware to all routes under a specific controller, you can add it to the `@controller()` decorator.

```typescript
@controller("/app", express.json())
export class AppController {
    @Post("/create")
    createApp() {
        return "Create App";
    }

    @Patch("/update")
    updateApp() {
        return "Update App";
    }
}
```

## Creating Custom ExpressoTS Middleware

To create a custom middleware, you need to extend the `ExpressoMiddleware` class and implement the `use` method.

```typescript
class CustomMiddleware extends ExpressoMiddleware {
    private isOn: boolean;

    constructor(isOn: boolean) {
        super();
        this.isOn = isOn;
    }

    use(req: Request, res: Response, next: NextFunction): void | Promise<void> {
        // Do something
        if (this.isOn) {
            next();
        } else {
            res.status(403).send("Forbidden");
        }
    }
}
```

Custom middleware allows you to pass parameters to the constructor and use them as options in the `use` method of your middleware.

Use ExpressoTS CLI to scaffold a custom middleware. CLI command to scaffold a custom middleware:

```bash
expressots g m <<middleware-name>>
```

## Controllers

Controllers act as the primary interface between the client and server in ExpressoTS applications. They handle incoming requests, validate payloads against an input DTO, and emit responses in the DTO pattern. In essence, controllers bridge the communication between clients and service layers, also known as use-cases.

**Controller MVC Pattern Example**

![Controller MVC Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao439adyqcz2rc70ewpu.png)

**Controller Single Responsibility Pattern Example**

![Controller Single Responsibility Pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11f7oq2we5httxxtesle.png)


In order to create a basic controller we create a class and use the `@controller()` decorator to define the route for the controller. Then we create a method and use the `http Methods` decorators to define the route and HTTP method for the method.

## Controller Class

### MVC Pattern

Multiple routes in a single controller.

```typescript
@controller("/user")
export class UserController {
    @Post("/")
    create(@response() res: Response) {
        return res.send("User created");
    }

    @Get("/")
    getUser(@response() res: Response) {
        return res.send("User listed");
    }
}
```

### Single Responsibility Pattern

One controller, one execution method, and one use case.

```typescript
@controller("/user/create")
export class UserCreateController {
    @Post("/")
    execute(@response() res: Response) {
        return res.send("User created");
    }
}
```

Both, the `@controller()` and `http methods` decorators works in combination to define the route and middlewares to be used in the endpoint.

## Controller Scope

The default scope of a controller is `Request`, as it is inherited from the `AppContainer` and default `Module` class scope. However, you can override the scope of a controller by using the `@scope()` decorator. This decorator accepts the same BindingScopeEnum enum values.

If you define the module scope you can not override it in a specific controller by using the `@scope` decorator.
The `@scope` decorator can only be used in specific controllers if the module scope is not defined.

Here is an example of usage:

```typescript
@scope(BindingScopeEnum.Singleton)
@controller("/")
class CreateUserController extends BaseController {}
```

The controller above will be scoped as `Singleton` and will be shared across all requests.

## BaseController Class

We also power a controller class with the `BaseController` class that offers in the constructor a parameter that the developer can indicate what service or domain he is currently building. This helps the log system throw errors with more information about the context of the error and where the error occurred.

Another advantage of using the `BaseController` class is that it offers a method in two different versions async and sync, which is the `callUseCase() or callUseCaseAsync()`.

These methods reinforce the idea of one use case per controller, and they are responsible for calling the use case that will implement the business logic of the controller.

The callUseCase methods, available in both synchronous and asynchronous versions, are well-suited for returning status and JSON responses to clients, as the users just need to pass the useCase return, res:Response, and statusCode.

The signature of the `callUseCase` method:

```typescript
callUseCase(useCase: any, res: any, successStatusCode: number);
```

-   `useCase`: The use case that will be called. This use case is injected in the controller constructor.
-   `res`: The response object from the express request.
-   `successStatusCode`: The status code that will be sent to the client if the use case is executed successfully. Please see the **[Http Status Code](./status-code.md)** type for more information.

The default response:

```typescript
return res.status(successStatusCode).json(dataReturn);
```

A more complete example of a controller class inheriting from the **BaseController** class is shown below:

```typescript
@controller("/")
class AppController extends BaseController {
    constructor(private appUseCase: AppUseCase) {
        super("app-controller");
    }

    @httpGet("/")
    execute(@response() res: any): IAppResponseDTO {
        return this.callUseCase(this.appUseCase.execute(), res, StatusCode.OK);
    }
}
```

## Controller Validating DTO

Read the follow section for DTO Validation :: https://doc.expresso-ts.com/docs/providers/dto-validator