Exportando rotas dinamicamente com Express

O Express facilita bastante a criação de rotas com NodeJS, e por ser bastante flexível, podemos implementar do nosso jeito e fazendo nosso jeito do jeito certo pode evitar muita dor de cabeça no futuro.

Vez ou outra nos deparamos com necessidades que vão além da stack principal dos nossos sistemas. São bibliotecas que não dão suporte à linguagem. São funcionalidades que despenderiam um esforço maior se fossem desenvolvidas com a stack do projeto…e por aí vai.

Nesse cenário, nos deparamos com a necessidade de algo bem simples que demandaria menos esforço se fosse feito com nodejs. O que precisávamos era passar alguns parâmetros para um endpoint e esse retornar uma coleção de dados e o resto seria feito com js do lado do cliente.

Dentre as muitas alternativas, a que se mostrou mais eficaz e rápida foi o Express. E pensando em escalar, combinados com TypeScript.

Estruturando o projeto:

A estrutura do projeto ficou assim:

  • src
    • controllers
    • middleware
    • routes
    • services

Criar rotas com Express é extremamente simples, mas pode se tornar uma tarefa maçante se for estruturado pensando em escalabilidade e começar a separar as coisas para os seus devidos lugares.

Então, para trabalhar com mais fluidez, deixamos as rotas todas em um mesmo diretório.

Primeiro foi exportado um array com as rotas seguindo o seguinte padrão:

const routes:Array<Array<string>> = [
  ['GET /', 'home@index',’meddlewareOpcional’],
]
export default routes
  • Método http
  • controller@index //sim, parecido com rails
  • Middleware opcional (serve para validar token, acesso etc)

Ainda dentro do diretório routes, exporta uma função que valida o padrão das rotas setadas no array, retornando um objeto contendo a url, o método http, o método do controller, o caminho do controller e o caminho do middleware, estes últimos servirão para o import dinâmico.

export const bootRoute = (route: Array<string>)=>{
  if(route.length < 2){
    throw(`Erro: A rota deve conter um array de 2 posições\n${JSON.stringify(route)}`)
  }
  const [routePath, action, middlewarePath] = route

  if(!routePath.match('/')){
    throw(`Rota sem url definida\n${JSON.stringify(route)}`)
  }

  if(!action.match('@')){
    throw(`Configure o Controller com @\n${JSON.stringify(route)}`)
  }
  const [controller, actionController] = action.split('@')
  let [httpMethod, url] = routePath.split(' ')

  if( !url ){
    url = httpMethod
    httpMethod = 'get'
  }
  httpMethod = httpMethod.toLocaleLowerCase()

  return {
    url,
    httpMethod,
    actionController,
    pathController: `${controller}_controller`,
    middlewarePath: middlewarePath ?? 'defaultMiddleware'
  }
}

No index.ts, serão feitos os imports dos controllers e meddlewares juntamente com os tipos e o router do express. A função setRoute (que poderia ficar em outro arquivo) configura as rotas do express de acordo com método http informado passando url, o middleware e o controller e exporta para ser usada nas inicialização do servidor

import { Router, NextFunction, Request, Response } from "express"
import routes from "./routeConfig"
import { bootRoute } from "./bootRoute"

interface Route {
  url: string;
  httpMethod: string;
  controller: any;
  actionController: string;
  middleware:(req: Request, res: Response, next: NextFunction) => void;
}


routes.map(async (route:Array<string>)=>{
  const { httpMethod, url, actionController, pathController, middlewarePath } = bootRoute(route)

  try {
    const { default: classController }  = await import(`../controllers/${pathController}`)
    const controller = new classController()
    const middlewareImporte  = await import(`../middleware/${middlewarePath}`)
    const middleware = middlewareImporte.default
    if( controller[actionController] ){
      setRoute({
        httpMethod,
        url,
        controller,
        middleware,
        actionController
      })
    }

  } catch (error) {
    console.error("Erro", error)
  }
})


const routeApp = Router()

const setRoute=(route: Route)=>{

  const {url, httpMethod, controller, actionController, middleware} = route

  const controllerHendler = controller[actionController].bind(controller)

  switch(httpMethod){
    case 'post':
      routeApp.post(url, middleware, controllerHendler)
    break
    case 'put':
      routeApp.put(url, middleware, controllerHendler)
    break
    case 'delete':
      routeApp.delete(url, middleware, controllerHendler)
    break
    default:
      routeApp.get(url, middleware, controllerHendler)
    break
  }
}

export {
  routeApp
}

Com essa estrutura garantimos que nosso arquivo de configuração do servidor, no nosso caso, index.ts, fique bem enxuto:


import express from 'express'
import { config } from 'dotenv'
import { routeApp } from './routes'
config()
const app = express()
const port = process.env.PORT || 80


app.use(routeApp)


app.listen(port, () => {
  console.log(`${port}`)
})

Podemos então trabalhar exportando rotas de um único local e nos concentrarmos nos controllers

import { Request, Response } from "express";

export default class HomeController{

  index(req: Request, res: Response){
    res.send({message:'E aí, jovens!'})
  }

}

O Express facilita bastante a criação de rotas com NodeJS, e por ser bastante flexível, podemos implementar do nosso jeito e fazendo nosso jeito do jeito certo pode evitar muita dor de cabeça no futuro.

#htt
Até a próxima!!!

0 Comentário