API Node Desacoplada: Criando os AdaptRouter para o Express e o Fastify
Tags: typescript, node, api, webdev
Published: 2023-08-01 21:34:00

Neste artigo vamos criar os adaptadores de rota para o `express` e para o `fastify`. Com isso nossa aplicação finalmente será capaz de funcionar com express ou fastify, apenas alterando o valor da variável `SERVER_TYPE` no arquivo `.env`.   

Dentro da pasta `infra` crie uma nova pasta chamada `adapter`. Dentro da pasta adapter crie dois arquivos `adaptRouteFastify.ts` e `adaptRouteExpress.ts`.

## Criando o adaptador para o Express
No arquivo `adaptRouteExpress.ts` insira o código abaixo:

```ts
import { Request, Response } from 'express'
import { HttpRequest } from '../shared/protocols/http'
import { IController } from '../shared/protocols/controller'

export const adaptRouteExpress = (controller: IController) => {
  // A função adaptRouteExpress é exportada para que possa ser 
  // utilizada em outros módulos da aplicação. Ela recebe um 
  // parâmetro controller, que é uma instância da classe que 
 //  implementa a interface IController.

  return async (req: Request, res: Response) => {
    // Retorna uma função assíncrona que será utilizada como 
    // callback para o roteamento no Express.js. A função callback 
    // recebe os parâmetros req (requisição) e res (resposta) do 
    // Express.js.

    const httpRequest: HttpRequest = {
      body: req.body
    }

    // Estamos adaptando a estrutura de dados da requisição 
    // recebida pelo Express.js para o formato esperado pela 
    // interface HttpRequest. Neste caso, estamos apenas pegando o 
    // corpo da requisição (req.body) e atribuindo ao campo body 
    // do objeto httpRequest.

    const httpResponse = await controller.handle(httpRequest)
    res.status(httpResponse.statusCode).json(httpResponse.body)

    // A resposta obtida do controller é adaptada para o formato 
    // esperado pelo Express.js. Utilizamos os métodos status e 
    // json do objeto res para definir o status code HTTP da 
    // resposta e enviar o corpo no formato JSON para o client.
  }
}
```
Agora precisamos utilizar o adapter criado. No arquivo `signUp.routes.ts` faça as alteração necessárias para ultilizar o adaptador:
 
Antes:

```ts
import { Router } from 'express'
import { CreateSignUpFactory } from '../../../application/modules/SignUp/useCases/CreateSignUp/CreateSignUpFactory'
const signupRoutes = Router()

signupRoutes.post('/signup', (req, res) =>  CreateSignUpFactory().handle(req, res))

export { signupRoutes }
```
Depois:

```ts
import { Router } from 'express'
import { CreateSignUpFactory } from '../../../application/modules/SignUp/useCases/CreateSignUp/CreateSignUpFactory'
import { adaptRouteExpress } from '../../../infra/adapters/adaptRouteExpress'
const signupRoutes = Router()

signupRoutes.post('/signup', adaptRouteExpress(CreateSignUpFactory()))

export { signupRoutes }
```
## Criando o adaptador para o Fastify

No arquivo `adaptRouteFastify.ts` insira o código abaixo:

> _Aqui a estrutura é a mesma com uma pequena diferença: a tipagem usada é a do fastify naturalmente._
```ts
import { FastifyRequest, FastifyReply } from 'fastify'
import { HttpRequest } from '../shared/protocols/http'
import { IController } from '../shared/protocols/controller'

export const adaptRouteFastify = (controller: IController) => {
  return async (req: FastifyRequest, res: FastifyReply) => {
    const httpRequest: HttpRequest = {
      body: req.body
    }
    const httpResponse = await controller.handle(httpRequest)
    res.status(httpResponse.statusCode).send(httpResponse.body)
  }
}
```
Agora precisamos utilizar o adapter criado. No arquivo `index.ts` que fica dentro da pasta `fastify` responsável pelas rotas(src/infra/routes/fastify/index.ts) faça as alteração necessárias para ultilizar o adaptador:

Antes:

```ts
import { FastifyPluginAsync } from 'fastify'
import { CreateSignUpFactory } from '../../../application/modules/SignUp/useCases/CreateSignUp/CreateSignUpFactory'


export const signUpRouter: FastifyPluginAsync = async (
  fastify
): Promise<void> => {
  fastify.post('/signup', (req, res) => CreateSignUpFactory().handle(req, res))
}
```

Depois:

```ts
import { FastifyPluginAsync } from 'fastify'
import { CreateSignUpFactory } from '../../../application/modules/SignUp/useCases/CreateSignUp/CreateSignUpFactory'
import { adaptRouteFastify } from '../../../infra/adapters/adaptRouteFastify'

export const signUpRouter: FastifyPluginAsync = async (
  fastify
): Promise<void> => {
  fastify.post('/signup', adaptRouteFastify(CreateSignUpFactory()))
}
```
## Testando a aplicação

Agora que já temos os adaptadores das rotas podemos testar se nossa aplicação vai funcionar tanto com `Express` quanto com `Fastify`.

### Testando com Express
Coloque o valor `express` para a variável `SERVER_TYPE` no arquivo `.env`

```ts
SERVER_TYPE=express
PORT_SERVER=5000
```
Execute o comando `yarn start:dev` no terminal. Teste a requisição no insomnia ou qualquer outra ferramenta, se tudo estiver correto teremos esse resultado:

![resultado requisição](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/285o7ir5rajbuhggz5mx.png)

O status code retornado é o 201 que foi definido na função create no arquivo `httpHelper.ts` o body stá vázio porque não definimos mensagem de criação bem sucedida.

### Testando com Fastify

Pare a aplicação e altere o valor da variável `SERVER_TYPE` para fastify(no arquivo `.env`).

Execute novamente o comando `yarn start:dev` no terminal. Teste a requisição no insomina ou qualquer outra ferramenta. O resultado obtido deverá ser o mesmo do anterior demonstrado no print acima.
 
## Conclusão

Como prometido temos agora uma aplicação que funciona tanto com `fastify` quanto com `express` mudando apenas uma variável de ambiente(`SERVER_TYPE`), sem ser necessário alterar nosso código principal. Nossa camada de domínio(entidades e usecases dentro da pasta `aplication`) não precisa saber que ferramentas externas estamos usando, isso não interessa.

Se quisermos usar outro framework que não o express ou o fastify não será necessário alterar código na nossa camada de domínio, apenas na camada de infraestrutura.

Ainda temos alguma coisas pra melhorar na nossa aplicação que será feito nos próximos artigos. Até breve.