Criando uma API SQL para Twitter com Typescript - Parte 2: Configurando as Rotas do Express
Published: 2021-02-23 22:56:24

Na [Parte 1: Configurando o servidor Express](https://dev.to/augustoasilva/criando-uma-api-sql-para-twitter-com-typescript-parte-1-configurando-o-servidor-express-f5k) eu mostrei como montar um servidor rápido em Express, como colocar os _types_ e converter o projeto para Typescript. Hoje eu irei mostrar 3 maneiras de adicionar rotas no nosso projeto, onde iremos do jeito funcional e depois iremos para orientação à objetos.

## Adicionando rotas: modificando o arquivo index.js na raíz do projeto!

A maneira mais fácil de adicionar rotas, seria adicionando-as diretamente no arquivo de entrada da nossa aplicação, como a seguir:

```javascript
import express from 'express'

const app = express()
const PORT: number = 3000 // Aqui você pode usar outra porta caso a 3000 esteja sendo utilizada.

// Aqui estão nossas rotas!
app.get('/', (req,res) => res.send('Servidor com Express + Typescript!'))

app.listen(PORT, () => {
  console.log(`O servidor está rodando em http://localhost:${PORT}`)
})
```

Esta é a maneira mais fácil de adicionar as rotas. Um contra ponto desta maneira é que o nosso arquivo **index.js** pode crescer muito rápido com as rotas e outras configurações que possamos adicionar. Sendo assim, vamos para uma próxima aproximação à este problema.

## Adicionando rotas: criando uma pasta com as rotas separadas!

Na pasta raiz do nosso projeto vamos criar uma pasta chamada **routes** com um arquivo dentro chamado **index.js**. Nossa estrutura deve estar algo mais ou menos assim:

* node_modules/
* routes/
  * index.js
* index.js
* packge.json
* packge-lock.json
* tsconfig.json

Agora vamos editar o nosso arquivo **routes/index.js**

```javascript
import { Router } from 'express'

// Aqui passamos o Router do express.
const router = Router()

// Aqui estamos adicionando nossa rota ao Router.
router.get('/', (req,res) => res.send('Servidor com Express + Typescript!'))

// E finalmente aqui estamos exportando para usar o nosso Router em outro arquivo.
export default router

```

Voltando para o nosso arquivo principal, faremos a seguinte modificação:

```javascript
import express from 'express'
import indexRoutes from './routes/index' // Importamos nosso arquivo de rotas.

const app = express()
const PORT: number = 3000 // Aqui você pode usar outra porta caso a 3000 esteja sendo utilizada.

// Aqui estão nossas rotas e colocaremos para que a nossa aplicação possa usá-las!
app.use(indexRoutes)


app.listen(PORT, () => {
  console.log(`O servidor está rodando em http://localhost:${PORT}`)
})
```

Pronto, dessa maneira temos um arquivo de rotas separado! Isso já melhora as coisas, não é mesmo? Mas existe o caso em que nosso arquivo de rotas também pode ficar muito grande caso criemos uma API com vários endpoints. Vamos então para a terceira e última forma de criar rotas, usando agora orientação à objetos. Estamos usando typescript para isso, não é mesmo?

## Adicionando rotas: criando um classe mãe para as rotas e também as classes de rotas!

Dentro da nossa pasta **routes** vamos criar 3 novos arquivos:

1. base-routes.ts
2. home-routes.ts
3. query-routes.ts

Nossa nova estrutra deve estar parecendo com algo assim:

* node_modules/
* routes/
  * index.ts
  * base-routes.ts
  * home-routes.ts
  * query-routes.ts
* index.ts
* packge.json
* packge-lock.json
* tsconfig.json

No nosso arquivo **base-routes.ts** teremos:

```javascript
/**
 * Interface criando o tipo Route.
 * 
 * O tipo Route deve seguir essa estrutura:
 * { 
 *   path: string, 
 *   type: string, 
 *   middlewares: Array<any>, 
 *   controllerFunction: any
 * }
 * 
 */
export interface Route {
  path: string,
  type: string,
  middlewares: Array<any>,
  controllerFunction: any
}

/**
 * Classe mãe para servir de base para a criação das rotas.
 */
export class BaseRoutes {
  /**
   * Variável que guarda o caminho da URL base.
   * 
   * Exemplo, se a url for www.meusite.com, e o basePath for '/api', a url criada será:
   * www.meusite.com/api
   * 
   * Esse basePath, será preposto às rotas que criaremos. Se tivermos uma rota: '/users' a rota do exemplo ficará:
   * www.meusite.com/api/users
   */
  private basePath: string
  // private router: Router = Router()

  public constructor(basePath: string) {
    this.basePath = basePath
  }

  /**
   * Método getter para a variável basePath.
   */
  public getBasePath (): string {
    return this.basePath
  }

  /**
   * Método setter para a variável basePath.
   * 
   * Deve ser utilizado antes de setar as rotas na inicialização da aplicação.
   * 
   * @param newBasePath novo basePath para ser utilizado.
   */
  public setBasePath (newBasePath: string): string {
    return this.basePath = newBasePath
  }

}
```

Agora criaremos em seguida os arquivos **home-routes.ts**:

```javascript
import { Router, Request, Response } from 'express'
import { Route, BaseRoutes } from './base-routes'

/**
 * Classe que conterá as rotas referentes à home/raiz, ou path '/'.
 */
export class HomeRoutes extends BaseRoutes {

  /**
   * Variável que receberá o Router do express.
   */
  private router: Router = Router()

  /**
   * Array contendo as rotas com o path, tipo, middlewares e funções.
   *
   * Declaramos aqui todas as nossas rotas referente ao path '/'.
   */
  private routes: Array<Route> = [
    {
      path: "/",
      type: "get",
      middlewares: [],
      controllerFunction: (req: Request, res: Response) => {
        res.send({ message: "Rota \'/api/v1/\' recebeu o método GET !" })
      }
    },
    {
      path: "/",
      type: "post",
      middlewares: [],
      controllerFunction: (req: Request, res: Response) => {
        res.send({ message: "Rota \'/api/v1\' recebeu o método POST !" })
      }
    }
  ]

  constructor(basePath: string) {
    super(basePath)
  }

  /**
   * Seta as rotas e as coloca no Router do express.
   * 
   * @param router Router da aplicação Express para adicionar rotas nele.
   * @returns uma flag que indica se o setup aconteceu 'true', ou não aconteceu 'false'.
   */
  public setup (router: Router): boolean {
    this.router = router
    const routes: Array<Route> = this.routes

    if (routes.length === 0) {
      return false
    }

    routes.forEach((element: Route) => {
      if (element.type === "get") {
        router.route(element.path).get(element.middlewares, element.controllerFunction)
      }

      if (element.type === "post") {
        router.route(element.path).post(element.middlewares, element.controllerFunction)
      }

      if (element.type === "put") {
        router.route(element.path).put(element.middlewares, element.controllerFunction)
      }

      if (element.type === "delete") {
        router.route(element.path).delete(element.middlewares, element.controllerFunction)
      }

    });

    return true
  }

  /**
   * Método getter da variável router.
   * 
   * @returns o objeto Router do Express relacionado às rotas da classe.
   */
  public getRouter (): Router {
    return this.router
  }

  /**
   * Método getter da variável routes.
   * 
   * @returns as rotas relacionadas à classe.
   */
  public getRoutes (): Array<Route> {
    return this.routes
  }

  /**
   * Método setter para a variável basePath.
   * 
   * Deve ser utilizado antes de setar as rotas na inicialização da aplicação.
   * 
   * @param newRoutes array que contém as novas rotas a serem utilizadas.
   */
  public setRoutes (newRoutes: Array<Route>): void {
    this.routes = newRoutes
  }

}
```

E **query-routes.ts**:

```javascript
import { Router } from 'express'
import { Route, BaseRoutes } from './base-routes'

export class QueryRoutes extends BaseRoutes {

  /**
   * Variável que receberá o Router do express.
   */
  private router: Router = Router()

  /**
   * Array contendo as rotas com o path, tipo, middlewares e funções.
   * 
   * Declaramos aqui todas as nossas rotas referente ao path '/query'.
   */
  private routes: Array<Route> = [
    {
      path: "/query",
      type: "get",
      middlewares: [],
      controllerFunction: (req: Request, res: Response) => {
        res.send({ message: "Rota \'/api/v1/query/\' recebeu o método GET !" })
      }
    }
  ]

  constructor(basePath: string) {
    super(basePath)
  }

  /**
   * Seta as rotas e as coloca no Router do express.
   * 
   * @param router Router da aplicação Express para adicionar rotas nele.
   * @returns uma flag que indica se o setup aconteceu 'true', ou não aconteceu 'false'.
   */
  public setup (router: Router): boolean {
    this.router = router
    const routes: Array<Route> = this.routes

    if (routes.length === 0) {
      return false
    }

    routes.forEach((element: Route) => {
      if (element.type === "get") {
        router.route(element.path).get(element.middlewares, element.controllerFunction)
      }

      if (element.type === "post") {
        router.route(element.path).post(element.middlewares, element.controllerFunction)
      }

      if (element.type === "put") {
        router.route(element.path).put(element.middlewares, element.controllerFunction)
      }

      if (element.type === "delete") {
        router.route(element.path).delete(element.middlewares, element.controllerFunction)
      }

    });

    return true
  }

  /**
   * Método getter da variável router.
   * 
   * @returns o objeto Router do Express relacionado às rotas da classe.
   */
  public getRouter (): Router {
    return this.router
  }

  /**
   * Método getter da variável router.
   * 
   * @returns o objeto Router do Express relacionado às rotas da classe.
   */
  public getRoutes (): Array<Route> {
    return this.routes
  }

  /**
   * Método setter para a variável basePath.
   * 
   * Deve ser utilizado antes de setar as rotas na inicialização da aplicação.
   * 
   * @param newRoutes array que contém as novas rotas a serem utilizadas.
   */
  public setRoutes (newRoutes: Array<Route>) {
    return this.routes = newRoutes
  }

}
```

Agora modificamos o nosso arquivo **routes/index.ts** para:

```javascript
import { Express, Router } from 'express'
import { HomeRoutes } from './home-routes'
import { QueryRoutes } from './query-routes'

// Setamos o nosso basePath padrão da nossa API
const basePath = "/api/v1"

// Criamos os nossos objetos que conterão as nossas rotas.
const homeRoutes: HomeRoutes = new HomeRoutes(basePath)
const queryRoutes: QueryRoutes = new QueryRoutes(basePath)

/**
 * Inicializa as rotas.
 * 
 * @param app objeto que contém a aplicação do Express.
 */
function initialize (app: Express): void {

  // Setando as rotas no Router do Express.
  homeRoutes.setup(Router())
  queryRoutes.setup(Router())

  // Colocando nossa aplicação para utilizar as nossas rotas recém adicionadas ao Router.
  app.use(homeRoutes.getBasePath(), homeRoutes.getRouter())
  app.use(queryRoutes.getBasePath(), queryRoutes.getRouter())
}

export { initialize }
```

E por fim alteramos nosso arquivo de entrada da aplicação para:

```javascript
import express from 'express'
import * as routes from './routes/index'

const app = express()
const PORT: number = 3000

// Inicializando o arquivo de rotas.
routes.initialize(app)

app.listen(PORT, () => {
  console.log(`Servidor rodando em http://localhost:${PORT}`)
})
```

## Bônus: adicionando o BodyParser ao nosso projeto.

Agora que temos as nossas rotas prontas, e também podemos adicionar mais se quisermos, iremos adicionar o BodyParser ao nosso projeto. Agora que estamos aptos à receber requisições em nossa aplicação vamos analises o body das solicitações que recebermos através desse _middleware_ do Express.

Vamos rodar o seguinte comando para instalar a bibloteca **body-parser**

```
npm install --save body-parser
```

Agora no nosso arquivo de entrada, faremos as seguintes modificações:

```javascript
import express from 'express'
import bodyParser from 'body-parser'
import * as routes from './routes/index'

const app = express()
const PORT: number = 3000

// Aplicando o parser de JSON em todas as rotas.
app.use(bodyParser.json())

// Inicializando o arquivo de rotas.
routes.initialize(app)

app.listen(PORT, () => {
  console.log(`Servidor rodando em http://localhost:${PORT}`)
})
```

## E agora, qual o próximo passo?

O próximo passo agora é adicionarmos os **controllers** para acessarmos a API do twitter.

Caso queira acompanhar o projeto em tempo real, você pode acessar [o repositório no Github aqui](https://github.com/augustoasilva/twit-sql) onde faço atualizações no projeto antes de escrever os artigos aqui.