Como Construir a API do NodeJS com NodeJS, Express, e MySQL

Com conhecimento de JavaScript e MySQL, nós podemos construir nossa API do NodeJS usando Express.

Fiz alguma pesquisa, e eu estava tentando desenvolver uma API a partir do zero.
Eu gosto de simplificar as coisas e tentar evitar duplicação de código.

Este Guia irá mostrar como construir uma API do zero:
Vocês irão aprender como criar rotas,
como usar o mysql2, como configurar e conectar ao banco de dados, e como executar consultas com instruções preparadas.
Como criar um middleware que possa obter um argumento adicional além de req, res, e próximo callback.
Vocês aprenderão como verificar os dados do objeto de requisição usando o módulo Express Validator.
Você aprenderá como usar o módulo JWT para criar um token para o usuário, verificar o token, e obter o objeto armazenado no token.
Além disso, você aprenderá como fornecer aos usuários permissão para acessar uma determinada rota com base em suas funções de usuário.

Tecnologias e Pacotes:

  • NodeJS
  • Express
  • mysql2
  • bcryptjs
  • jsonwebtoken
  • express-validator
  • dotenv
  • cors

Instalando o MySQL:

Eu uso WSL, e você pode usar este tutorial para ver como instalar o MySQL em WSL.
Você precisa ter certeza de que o MySQL está rodando com este comando:

sudo service mysql status
Entre no modo tela cheia Sair do modo tela cheia

Se não estiver rodando, basta usar:

sudo service mysql start
Entrar no modo de ecrã inteiro Sair do modo de ecrã inteiro

Visão geral da aplicação:

Construiremos uma API de repouso para operações CRUD: criar, ler, atualizar e excluir usuários.

Criar a pasta do projeto e instalar todas as dependências:

mkdir mysql-node-express && cd mysql-node-expressnpm init -ynpm i express express-validator mysql2 cors dotenv jsonwebtoken -Snpm i nodemon -D
Entrar no modo tela cheia Sair do modo tela cheia

Ir para o arquivo package.json e alterar o valor “principal” para “src/server”.js” e adicione estes scripts ao script object:

"start": "node src/server.js","dev": "nodemon"
Entre no modo tela cheia Sair do modo tela cheia

package.json deve se parecer com isto:

Criar arquivo .env:

Utilizaremos o ficheiro .env para gerir todas as variáveis do nosso ambiente.
O arquivo .env é um arquivo oculto que nos permite personalizar nossas variáveis de ambiente usando a sintaxe ENV VARIABLE = VALUE.
Estas variáveis são carregadas usando o módulo dotenv que já temos instalado.
O arquivo .env pode ser definido em diferentes estágios do ambiente (ambientes de desenvolvimento / estágio / produção).

Criar o arquivo .env, copiar as seguintes linhas, e atualizar o arquivo com seu db_name, db_username e senha do MySQL:

# DB ConfigurationsHOST=localhostDB_USER=db_usernameDB_PASS=db_passwordDB_DATABASE=db_name# local runtime configsPORT=3000SECRET_JWT=supersecret
Entrar no modo tela cheia Sair do modo tela cheia

Criar arquivo nodemon.json:

Nodemon é uma ferramenta que ajuda a desenvolver aplicações baseadas no node.js, reiniciando automaticamente a aplicação do nó quando forem detectadas mudanças de arquivo no diretório de destino.
O nodemon é um wrapper substituto para o nó. Ao invés de usar o comando node, devemos usar o comando nodemon na linha de comando para executar nosso script.
Podemos facilmente adicionar chaves de configuração enquanto executamos nodemon na linha de comando, como:

nodemon --watch src
Entrar no modo tela cheia Sair do modo tela cheia

Podemos também usar um arquivo (nodemon.json) para especificar todos os switches.
Se quisermos ver vários arquivos em um diretório, podemos adicionar o diretório no array “watch”.
Se quisermos procurar por uma extensão específica (como um arquivo ts) podemos usar a propriedade “ext”.
Se quisermos ignorar alguns arquivos, podemos defini-los no array “ignore”, e assim por diante…
Utilizo este arquivo principalmente quando estou criando um servidor com NodeJS baseado em typescript, mas acho que é mais fácil ter mais lugares para incluir nossas configurações de aplicativo.
Este arquivo é opcional.

Criar arquivo nodemon.json e adicionar isto ao arquivo:

{ "watch": , "ext": ".js", "ignore": }
Entrar no modo tela cheia Sair do modo tela cheia

Criar a pasta src:

mkdir src && cd src
Entrar no modo de ecrã inteiro Sair do modo de ecrã inteiro

Na pasta src criar subpastas: controladores, modelos, rotas, middleware, db, e utils:

mkdir controllers models routes middleware db utils
Entre no modo tela cheia Sair do modo tela cheia

Setup Express server:

No directório src criar o ficheiro server.js e copiar estas linhas:

Neste ficheiro, importamos express para construir as restantes APIs e usamos express.json() para analisar os pedidos recebidos com as cargas úteis JSON.

Também importamos o módulo dotenv para ler o ficheiro .env config para obter o número da porta para executar o servidor.

Também importamos userRouter.

Após isso, temos um middleware que lida com 404 erros → se alguém procurar por um endpoint que não existe, ele receberá este erro: ‘Endpoint Not Found’ com o código de status 404. Se next(err) for chamado, você pode ver o middleware 404 como um exemplo.
Nós ouvimos a porta do arquivo.env e a imprimimos no console que o servidor está rodando.

Criar banco de dados MySQL e tabela de usuário:

No diretório db, vamos criar o arquivo create-user-db.sql e copiar colar estas linhas:

Neste script, primeiramente, soltamos o banco de dados se ele existir para que ele possa ser redefinido rapidamente em caso de erro (você pode comentar esta linha se quiser), então, criamos o banco de dados se ele não existir. Definimo-la como a nossa base de dados activa e criamos uma tabela “utilizador” com todas as colunas (id, nome de utilizador, etc.), permitindo novamente um reset conveniente, se necessário. Você pode executar esta consulta no seu banco de dados cliente se você estiver usando um.

Se você estiver usando wsl, no diretório db você pode executar:

mysql -u -p < create-user-db.sql
Entrar em modo tela cheia Sair em modo tela cheia

Configurar e Conectar ao banco de dados MySQL:

Criar um ficheiro adicional no directório db chamadas db-connection.js, e copiar-colar isto:

Neste ficheiro, primeiro importamos o módulo dotenv e usamos para ler a informação de configuração da base de dados como db host, db user do ficheiro.env.

Verificamos a ligação no caso de haver um problema com a base de dados e depois libertamos a ligação.

Temos um método de consulta que retorna uma promessa do resultado da consulta.

Usamos um bloco try-catch para capturar erros comuns do MySQL e retornar códigos e mensagens de status HTTP apropriados.

No final do arquivo, criamos uma instância da classe DBConnection e usamos o método de consulta, e no model.js (que veremos no próximo passo), usaremos o método de consulta novamente.

Create Error Handler:

Próximo, vamos criar o nosso manipulador de erros.

Para isso, primeiro, vamos criar o arquivo HttpException.utils.js sob o diretório utils, e copiar colar o seguinte:

A classe HttpException herda a classe Error.
O construtor irá obter o status, mensagem e dados. Passaremos a variável message para o construtor pai usando super(message), e então inicializaremos as variáveis de status, mensagem e instância de dados.

Depois disso, criaremos um manipulador de erros de middleware no diretório middleware.
Criaremos um erro. middleware.js e copie-colar o seguinte:

Pode ver no fundo do arquivo como o objeto será.

O middleware terá req, res, e próximo callback, mas também terá um argumento adicional, erro (usando next(error) antes de chegarmos a este middleware).

Usamos desestruturação para obter as variáveis do objeto de erro e definir o status para 500 se ele não foi configurado antes.

Após isso, se o status é 500, vamos nos certificar de alterar a mensagem para que o usuário receba uma mensagem de erro genérica do servidor interno sem revelar a natureza exata da falha.

Após isso, criamos um objeto de erro com o tipo, status e propriedades da mensagem (os dados são opcionais).

Criar arquivos utils (helpers):

No diretório utils, criamos mais dois arquivos, common.utils.js, e userRoles.utils.js.

common.utils.js:

Esta função ajuda a definir múltiplos campos para consultas preparadas com pares de valores chave.
ColunaConfigure o array de pares de chaves =?,
Os valores devem portanto estar na mesma ordem que o array de conjuntos de colunas.

userRoles.utils.js:

module.exports = { Admin: 'Admin', SuperUser: 'SuperUser'}
Entrar no modo de tela cheia Sair da tela cheia

Criar a função Async:

Criar outro arquivo chamado awaitHandlerFactory.middleware.js no diretório middleware e copiar-colar isto:

Em geral, sabemos que o middleware é apenas um método assíncrono que obtém o req, o res e os próximos argumentos, então, se quisermos que este middleware obtenha um argumento adicional, faremos desta forma (usaremos isto também no auth middleware no próximo passo).

Esta função irá obter um callback, executar o script middleware, e irá tentar acionar este callback no bloco try.
Se algo der errado aqui, ele irá pegar o erro e nós usaremos o next(err) (que irá transferi-lo para o próximo middleware => error.middleware.js).

Create Authentication Middleware:

Outro middleware que precisamos é o auth middleware que vamos usar para verificar as permissões do usuário através do módulo JWT.

Similar para aguardarHandlerFactory.middleware.js middleware, temos aqui um middleware que requer um argumento adicional (que é opcional) => roles.

Utilizei o try-catch para ajustar o status do erro na área de captura para 401 (se o token expirou, por exemplo).

No início, estamos procurando por req.headers.authorization – se ele não está definido no cabeçalho ou se o cabeçalho não começa com “Bearer”, o usuário receberá uma resposta 401. Se começar com “Bearer”, vamos obter o token e usar a chave secreta do arquivo.env para decifrá-lo.

Verificaremos o token usando a função jwt.verify sincronizada, que obtém o token, e a chave secreta, como argumentos e retorna a carga útil decodificada, se a assinatura é válida e os campos opcional expiração, audiência ou emissor são válidos. Caso contrário, ele lançará um erro.

Agora, podemos encontrar o usuário com este token pesquisando o id.
Se o usuário não existir mais, ele obterá uma exceção de 401 sem nenhuma informação.
Se o usuário existir, vamos verificar se o usuário atual é o proprietário que está procurando por suas rotas ou se o usuário tem a função de acessar esta rota.
Estamos salvando o usuário atual, caso ele queira obter seus dados no próximo middleware (como a rota “whoami”).

Validação de dados usando o módulo Express Validator:

No diretório middleware, vamos criar um arquivo adicional que vamos usar para verificar as propriedades req.body.

Criar uma subpasta no diretório middleware chamada validators e criar um arquivo neste diretório, userValidator.middleware.js. Copiar-colar isto:

Neste arquivo, eu usei o módulo express-validator, que é muito fácil de usar sempre que precisamos verificar algumas propriedades, verificar se a propriedade existe, ou criar verificações personalizadas com uma mensagem personalizada para o usuário se algum valor de propriedade não é válido.

Agora podemos começar a criar nossos arquivos de rotas, controladores e modelos.

Definir Rotas:

Criar arquivo user.route.js no diretório de rotas e copiar-colar isto:

O exemplo acima mostra como definir rotas. Vamos tentar quebrá-lo em pedaços:

  • Você pode criar um roteador usando express.Router().Cada rota pode carregar uma função middleware que lida com a lógica do negócio.UserController, por exemplo, carrega todos os middlewares principais.Para usar o router, o router deve ser exportado como um módulo e usado na aplicação principal usando app.use(router_module).
  • Usamos auth middleware para autenticação e autorização do usuário, para verificar o token do usuário ou função do usuário para a rota.No nosso exemplo, algumas das rotas usam o auth middleware para verificar a autenticação e autorização do usuário. Este middleware será acionado antes do middleware principal (aquele que detém a lógica do negócio). O próximo callback deve ser chamado para passar o controle para o próximo método middleware. Caso contrário, a solicitação será deixada pendurada.
  • awaitHandlerFactory (try-catch middleware) é usado para envolver todo o middleware assíncrono. Desta forma, se um dos middlewares lançar um erro, awaitHandlerFactory irá apanhar esse erro. Você pode ver que todas as funções do nosso middleware estão envolvidas com o awaitHandlerFactory middleware, o que nos ajuda a lidar com nossos erros usando o try-catch em um só lugar.
  • Além disso, temos o createUserSchema, updateUserSchema e validateLogin schema para validar o corpo antes de iniciarmos o próximo middleware.

A sintaxe do método HTTP é:

Criar o Controlador:

Criar arquivo user.controller.js no diretório controller e copiar-colar isto:

Como mencionado acima, o arquivo controller contém nossa lógica de negócios para lidar com nossas rotas.
No nosso exemplo, alguns métodos usam a classe UserModel para consultar o banco de dados para obter os dados.
Para retornar os dados em cada middleware, usamos res.send(resultado) para enviar uma resposta ao cliente.

Criar o Modelo:

E criar o arquivo user.model.js no diretório models e copiar-colar isto:

Esta classe faz a conexão entre o controlador e o banco de dados.
Aqui temos todos os métodos que obtêm os argumentos do controlador, fazem uma consulta, preparam declarações, conectam ao banco de dados usando o método de consulta da classe db-connection, enviam a requisição com o array de declarações preparadas e recuperam o resultado.
Cada função retorna o resultado para o controlador.

.gitIgnore:

Caso você decida adicionar este projeto ao seu GitHub, não se esqueça de criar um arquivo .gitignore e copie colar isto:

node_modules.env
Entre no modo tela cheia Sair da tela cheia

Este arquivo apenas diz ao git quais arquivos ele deve ignorar.
Você deve evitar o diretório node_modules porque ele é pesado e não é necessário para o repositório.
Quando alguém clona este repositório, irá usar o comando “npm I” para instalar todas as dependências.
Ignorar o arquivo .env é para esconder suas configurações privadas de outros desenvolvedores usando seu código.

Deixe uma resposta

O seu endereço de email não será publicado.