JavaScript: Construindo um backlog de filmes com Node.js, Express e MySQL

Neste artigo vamos trabalhar a estruturação inicial de um sistema web para rastreamento de atividades. Imagine o seguinte cenário: você deseja registrar todos os filmes que você viu durante o ano, ou seja, você deseja rastrear (ou trackear) essa atividade. E é para isso que nosso sistema será utilizado.

Construiremos o sistema como uma API RESTful, utilizando o runtime Node.js, o framework Express e o sistema gerenciador de banco de dados MySQL. Caso não saiba o que é uma API RESTful, recomendo que assista o vídeo abaixo e depois continue o tutorial.

API RESTful em camadas

Nossa API seguirá o padrão Model-View-Controller (MVC), que nos permitirá uma organização eficiente do código e melhor manutenabilidade. Aliás, para ser mais correto, utilizaremos uma variação do padrão porque nosso foco será só a parte do back-end. Logo, não teremos as construções das views. Portanto, tirando a camada das Views (V), ficamos apenas com as camadas Model-Controller (MC).

Pré-requisitos

Para continuar este tutorial, você deve ter instaladas em sua máquina as seguintes ferramentas:

  • o runtime Node.js.
  • o gerenciador de pacotes NPM (instalado junto com o Node.js).
  • o banco de dados relacional MySQL configurado e funcionando.
  • o framework ExpressJS.
  • o mapeador objeto-relacional (ORM) Sequelize.

Pesquisando na internet você encontrará instruções sobre como instalar as ferramentas, especialmente nos próprios sites delas.

Estrutura de diretórios do projeto

/api-backlog
├── /src

| ├── /middlewares
| │ ├── authMiddleware.js
| ├── /controllers
| │ ├── filmeController.js

| │ ├── authController.js
| ├── /models
| │ ├── filmeModel.js

| │ ├── userModel.js
| ├── /routes
| │ ├── filmeRoutes.js

| │ ├── authRoutes.js
| ├── /config
| │ ├── dbConfig.js
| └── app.js
└── server.js

Iniciando o projeto

Abra o terminal de seu SO. No caso do OpenSuse, sistema usado por mim, é o Konsole. Em seguida, acesse a pasta do seu projeto (aqui chamamos de api-backlog) pelo terminal e digite o comando:

npm init

O processo será iniciado. Siga as instruções para criar um arquivo package.json que irá gerenciar as dependências do projeto. A cada solicitação feita na instalação, preencha os dados do projeto – ou deixe em branco o que não tiver resposta mesmo – e pressione Enter. Para ajudar nessa etapa, utilize a Figura 1 como exemplo. Nesta fase, destaco a configuração do entry point. Por padrão, o node sugere como ponto de entrada da aplicação o arquivo index.js. Contudo, neste projeto vamos utilizar o arquivo server.js.

Figura 1 – Processo de inicialização de um projeto usando o NodeJS com a configuração básica do package.json.

O gerenciador de pacotes npm é instalado junto com o Node.js. Entretanto, todas as demais dependências que precisarmos no projeto devem ser instaladas caso a caso. Então, em seguida, vamos instalar as dependências necessárias para este projeto. Execute no terminal o comando:

npm install express mysql2 sequelize body-parser jsonwebtoken bcryptjs

Certamente você não entenderá para que serve cada dependência agora, mas no decorrer do desenvolvimento você entenderá sua utilização dentro do sistema. Estamos instalando-as desde agora para as futuras chamadas no código.

Instaladas as dependências de nosso projeto, podemos partir para as demais configurações e iniciar a programação efetiva do sistema. Mas, antes de continuarmos, um aviso: não se preocupe caso apareça uma nova pasta em seu diretório chamada node_modules. Isso acontece porque nós instalamos os frameworks e clientes de BD que nosso sistema precisará durante o uso. Em outras palavras, as dependências. A Figura 2 é um exemplo de como ficará a pasta.

Figura 2 – Exemplo da pasta node_modules dentro do projeto.

Configurando o framework Express e o ponto de entrada da aplicação

Toda aplicação precisa de um ponto de entrada. Esse ponto costuma ser um arquivo ou função inicial que dá o start no restante das funções e tarefas.

Em nossa estrutura, definimos o arquivo server.js como sendo esse ponto. Nele, escreveremos o código a seguir para iniciar o servidor e configurar as rotas da API. Dessa forma, nossa app poderá receber e responder a requisições. Para entender a lógica da programação leia os comentários explicativos que estão no Código 1 enquanto o digita no seu arquivo server.js.

IMPORTANTE: Todas as explicações sobre códigos serão feitas nos próprios códigos, em forma de comentários (todo texto em cor cinza após os dois símbolos de barra)

Código 1 – Programação do arquivo de entrada da aplicação, de nome server.js.

Se você é uma pessoa atenta, deve ter percebido que no código do arquivo server.js nós importamos outro arquivo chamado app.js que não apresentamos ainda. Bem, este arquivo foi criado para que pudéssemos separar a inicialização do express, do body-parser e das rotas. Fizemos isso para melhorar a manutenção do código em caso de necessidade futura. Portanto, dentro do diretório src, crie o arquivo app.js e digite o Código 2.

Código 2 – Programação do arquivo de configuração do servidor ExpressJS, de nome app.js.

Configurando a conexão com o banco de dados

O banco de dados que iremos usar para esse projeto será baseado na modelagem da Figura 3. Vamos chamá-lo de backlog (tudo em letras minúsculas). Após analisar a modelagem dos dados para o sistema, você pode copiar o código SQL apresentado após figura para criar a tabela Filmes em seu banco.

Figura 3 – Modelagem conceitual e lógica do banco de dados que será utilizado pela aplicação.
CREATE TABLE Filmes (
    id int PRIMARY KEY,
    titulo varchar(50),
    diretor varchar(50),
    genero varchar(50),
    ano int
);

Neste ponto do processo temos um banco chamado backlog, uma tabela chamada Filmes e dois arquivos programados (server.js e app.js). Repare que na modelagem não temos uma tabela para os Usuários, mesmo que tenhamos visto arquivos relacionados à autenticação de usuários na estrutura de diretórios. Mas, por enquanto, manteremos assim mesmo. No final, essa questão será resolvida pelo ORM Sequelize.

Agora vamos configurar a conexão do sistema web com o banco de dados. Crie o arquivo dbConfig.js conforme vimos na estrutura de diretórios e transcreva o Código 3.

Código 3 – Programação do arquivo de conexão com o banco por meio do SGBD MySQL, de nome dbConfig.js.

Para cada aplicação, é necessário substituir os argumentos da função na linha 5. Para o nosso exemplo, utilizei os seguintes valores:

  • nome-do-banco foi trocado para ‘backlog‘.
  • nome-do-usuario foi trocado para ‘root‘.
  • senha-do-usuario foi trocado para ' ' (vazio). Normalmente as instalações do MySQL vem com a senha de root como vazias. Por isso, optei por manter o padrão para este exemplo. Contudo, numa aplicação real, você jamais utilizará usuário root ou senha vazia.

Você pode escolher colocar outros valores para estes argumentos, não faz diferença. Apenas lembre-se quais são eles para configurações futuras.

Criando as rotas (routes)

Por meio das rotas (routes) iremos determinar os endpoints que serão utilizados para enviar os dados aos respectivos controllers do sistema. Cada rota usa um verbo HTTP (GET, POST etc.) para informar de que tipo será a requisição (request) e a resposta (response) da função callback que programarmos. Além disso, precisamos definir qual será o caminho a ser acionado para ativar aquela rota – que pode ser apenas / , /:id etc.).

Caso você não sabia muito bem o que são os verbos HTTP, recomendo a leitura dos sites MDN Web Docs e DevMedia.

Seguindo a estrutura de diretórios definida, temos dois arquivos de rotas: um para as funções de autenticação (authRoutes.js) e outro para as funções relativas aos filmes (filmeRoutes.js). No Código 4 apresento a lógica para a criação das rotas relativas à autenticação dos usuários. Em seguida, no Código 5, estão as rotas para o arquivo dos filmes.

Código 4 – Programação do arquivo de rotas de autenticação de usuários, de nome authRoutes.js.
Código 5 – Programação do arquivo de rotas dos filmes, de nome filmeRoutes.js.

Criando o middleware de autenticação

Este arquivo é o ponto principal da estratégia de autenticação baseada em JSON Web Tokens (JWTs). Ele atua como um “porteiro” para as rotas protegidas, garantindo que apenas usuários autenticados e com tokens válidos possam acessá-las. O funcionamento básico do arquivo é o seguinte:

  1. Geração do Token: Quando um usuário faz login com sucesso (via authRoutes.js e authController.js), o servidor gera um JWT. Este JWT é assinado usando a SECRET_KEY e geralmente inclui o ID do usuário e, opcionalmente, outras informações. O token gerado é então enviado de volta ao cliente.
  2. Envio do Token pelo cliente: Para acessar rotas protegidas (como as rotas de filmes que usam este middleware), o cliente deve incluir o JWT em todas as requisições subsequentes. O padrão é enviá-lo no cabeçalho Authorization no formato Bearer <token>.
  3. Interceptação pelo Middleware: Quando uma requisição chega a uma rota que utiliza authMiddleware, este middleware é executado antes que a requisição chegue ao controlador da rota (por exemplo, filmeController).
  4. Validação do Token:
    • Ele primeiro verifica se o cabeçalho Authorization existe e está no formato correto (Bearer <token>).
    • Em seguida, ele usa jwt.verify() para decodificar o token. Essa é a etapa mais crítica, pois jwt.verify() faz duas coisas:
      • Verifica a Assinatura: Ele usa a SECRET_KEY para garantir que o token não foi adulterado desde que foi emitido. Se a assinatura não corresponder, o token é inválido.
      • Verifica a Expiração: Ele checa se o token não expirou.
  5. Acesso Autorizado ou Recusado:
    • Se o Token é Válido: O ID do usuário é extraído do token decodificado (decoded.id) e anexado ao objeto req (req.userId). Em seguida, next() é chamado, permitindo que a requisição continue para a função do controlador da rota (filmeController, por exemplo). Agora, o controlador sabe quem está fazendo a requisição.
    • Se o Token é Inválido/Ausente: Uma resposta de erro com status 401 Unauthorized é enviada de volta ao cliente, e a requisição é interrompida, nunca chegando ao controlador da rota.

No Código 6 apresento a programação para o arquivo authMiddleware.js, que será nosso “porteiro” de autenticação neste projeto.

Código 6 – Programação do arquivo middleware de autenticação para usuários, de nome authMiddleware.js

Neste ponto, cabe fazermos uma consideração importante: este projeto tem um cunho pedagógico. Ou seja, há diferenças em construir este sistema pensando de forma comercial. Para um sistema real em produção:

  • SECRET_KEY: NUNCA deixe a SECRET_KEY no código-fonte em um ambiente de produção. Use variáveis de ambiente – por exemplo process.env.JWT_SECRET – para carregar a chave de forma segura.
  • Expiração do Token: JWTs devem ter uma data de expiração para aumentar a segurança. A biblioteca jsonwebtoken lida com isso automaticamente se você definir a expiração ao criar o token.
  • Listas Negras/Revogação: Para tokens de curta duração, a simples expiração pode ser suficiente. Para tokens de longa duração ou para permitir que os usuários “desloguem” (invalidando o token antes da expiração), precisaríamos de um mecanismo de lista negra para armazenar tokens revogados.
  • Refrescar Tokens: Para uma melhor experiência do usuário e segurança, APIs frequentemente implementam tokens de acesso de curta duração e tokens de atualização (refresh tokens) de longa duração. O token de atualização é usado para obter um novo token de acesso quando o atual expira, sem a necessidade de o usuário fazer login novamente.

Criando os controladores (controllers)

Agora que já temos as rotas definidas para os usuários e os filmes e o middleware de autenticação, precisamos lidar com a lógica do sistema, também conhecida como regra do negócio. Na programação em camadas MVC, a camada dos controllers são os responsáveis por implementar a lógica do negócio na API.

Da mesma forma como fizemos para as rotas, criaremos um arquivo respectivo para cada entidade. Portanto, teremos um arquivo chamado authController.js e outro chamado filmeController.js. Os arquivos devem estar dentro do diretório controllers, seguindo a estrutura prevista.

Em cada arquivo controller definiremos as funções que serão capazes de lidar com os verbos do protocolo HTTP (GET, POST, PUT, DELETE). Nos Códigos 7 e 8 descrevi as programações dos arquivos authController.js e filmeController.js, respectivamente.

Código 7 – Programação do arquivo controller de autenticação para usuários, de nome authController.js
Código 8 – Programação do arquivo controller de funções para manipular os dados dos filmes, de nome filmeController.js

Criando os modelos (models)

A partir de agora, iremos criar os modelos para armazenamento dos dados no banco. De forma simplificada, podemos dizer que os modelos – do inglês, models – representam a estrutura das entidades no banco de dados. Em alguns casos, é possível encontrar a palavra schema como sinônimo.

Crie os arquivos userModel.js e filmeModel.js na pasta models conforme a estrutura definida para este projeto. Transcreva os Códigos 9 e 10 nos arquivos criados, respectivamente.

Código 9 – Programação do arquivo model para gerenciamento dos dados de usuários, de nome userModel.js.
Código 10 – Programação do arquivo model para gerenciamento dos dados de filmes, de nome filmeModel.js.

Testando a aplicação

Antes de mais nada, confirme que seu banco de dados MySQL está em execução. Se você estiver usando o XAMPP como servidor para o banco de dados e o sistema operacional OpenSuse, você pode inicializar o MySQL digitando o comando no terminal:

sudo /opt/lampp/lampp start

Lembrando que o caminho /opt/lampp/lampp é o local para o diretório de instalação na minha máquina. Você precisa verificar onde o XAMPP está instalado na sua (provavelmente será no mesmo local, mas é bom confirmar).

Entretanto, caso esteja utilizando contêineres Docker para rodar banco de dados do mesmo modo que eu descrevi neste artigo (clique aqui), então você deve iniciar o Docker para subir o contêiner. Utilize o comando abaixo:

sudo systemctl start docker

Com o banco de dados inicializado, vamos subir nosso servidor e colocar nossa aplicação para funcionar. No terminal, entre na pasta raiz do projeto (api-backlog) e digite:

node server.js

Se tudo estiver correto, você verá no terminal uma mensagem igual a que está demonstrada na Figura 4. Isso é sinal de que nosso servidor está rodando e pronto para receber requisições.

Figura 4 – Resultado que aparece no terminal quando a aplicação foi iniciada com sucesso.

Para testarmos o funcionamento das rotas de autenticação e de CRUD dos filmes, precisaremos de um programa específico para testes de APIs (o Postman, por exemplo). Portanto, vamos parar este artigo aqui e deixar a descrição dos testes desse sistema para outro post (clique aqui para acessar).

Conclusão

Agora sim, finalmente criamos e programamos todos os arquivos que havíamos previsto na estrutura inicial. Se você seguiu os passos deste tutorial corretamente, seu sistema deve estar pronto para o teste de funcionamento.

Depois de várias camadas, linhas de código, arquivos e pastas, chegamos ao fim do nosso simplório tutorial. Entretanto, antes que você saia do post, quero deixar dois disclaimers importantes:

  1. Sabemos que este código não é a versão mais otimizada ou segura possível para criar-se uma API RESTful. Entretanto, este tutorial foi elaborado para iniciantes. Logo, optamos pela facilidade de entendimento dos conceitos base para separação de código em camadas, especificamente o padrão Model-View-Controller (MVC).
  2. Lembre-se: o ideal é que você sempre digite seu próprio código, ajustando-o e adaptando-o para o seu tipo de escrita. Não fique dependente da ferramenta Copia e Cola. Assim, você estará reforçando sua aprendizagem.

Obrigado pelo seu tempo e bons estudos!