/API-Rest-Node-SOLID

Nesse projeto será desenvolvido uma aplicação para check-ins em academias. Aqui vamos aplicar sobre alguns conceitos do SOLID, Design Patterns, Docker para iniciar o banco de dados, JWT e Refresh Token, RBAC e diversos outros conceitos.

Primary LanguageTypeScriptMIT LicenseMIT

API Rest NodeJs com SOLID

SobreVitrine DevTecnologiasInstalaçõesFuncionalidadesDeployAutorLicença

 

OpenAPI Swagger

💻 Sobre o projeto

🚀 Neste projeto, será desenvolvido uma aplicação para check-ins em academias, aplicando conceitos do SOLID, Design Patterns, Docker para iniciar o banco de dados, JWT e Refresh Token, RBAC e diversos outros conceitos. O objetivo é fornecer um template completo e bem estruturado para o desenvolvimento de APIs RESTful com Node.js, Docker e Prisma, proporcionando uma base sólida para criar aplicações web escaláveis e de alta qualidade.

A primeira parte do projeto inclui a configuração de variáveis de ambiente para bancos de dados MySQL, utilizando o arquivo .env e o Prisma como ORM. As variáveis de ambiente são usadas para armazenar informações confidenciais, como credenciais de banco de dados, e para configurar o projeto de acordo com o ambiente de desenvolvimento ou produção.

Na segunda parte, é apresentada a arquitetura do projeto, utilizando a biblioteca Vitest para testes e cobertura de testes. Isso inclui a instalação de várias dependências, a criação de arquivos de configuração e a adição de scripts no package.json para executar testes e outras tarefas relacionadas.

A aplicação possui testes unitários de integração e testes e2e e uma cobertura de testes de 100%. Além disso, utilizamos o GitHub Actions para executar os testes unitários de integração e e2e e verificar a cobertura de testes a cada push na branch main.

Por fim, são apresentadas algumas funcionalidades adicionais e bibliotecas utilizadas no projeto, como bcryptjs para criptografia de senhas e dayjs para manipulação de datas. Além disso, o projeto inclui uma lista detalhada de requisitos funcionais e não funcionais, bem como regras de negócio que orientam o desenvolvimento das funcionalidades da aplicação.

Dessa forma, este projeto oferece um ponto de partida sólido para o desenvolvimento de aplicações web, incluindo configurações e ferramentas para garantir a qualidade do código e a manutenção do projeto ao longo do tempo.

 

issue site Template-Api-Rest-Node-Docker-Prisma total amount of programming languages used in the project most used language in the projects repository size

deploy badge Render swagger badge

 


 

📺 Vitrine Dev

🪧 Vitrine.Dev
✨ Nome API Rest NodeJs com SOLID
🏷️ Tecnologias NodeJs, TypeScript, JavaScript, .ENV, Fastify, PrismaJs, Zod, MySql, Docker, Vitest, GitHub Actions, Swagger, EsLint, Insomnia e JSON Web Token.

 

🛠 Tecnologias

As seguintes ferramentas foram usadas na construção do projeto

 

Node.js badge TypeScript badge JavaScript badge Fastify badge Dotenv badge Prisma badge ZOD badge Docker badge MySQL badge Vitest Badge Insomnia badge swagger badge JSON Web Tokens Badge vscode download badge github actions code formatter prettier code standardization eslint


 

⚙️ Instalações

 

Criando projeto NodeJs

# Create project nodejs with npm and -y to accept all default options
npm init -y
// Create scripts in package.json
"scripts": {
  "dev": "set NODE_ENV=dev&& tsx watch src/server.ts", // Create script to run server in development mode
  "build": "tsup src !src/**/*.spec.ts !src/**/test/**/* --out-dir build --minify --publicDir src/docs", // Create script to build server in production mode
  "start": "set NODE_ENV=production&& node build/server.js", // Create script to run server in production mode
},

Create .gitignore file

Create .npmrc file with save-exact=true to save exact version of dependencies

 

.env architecture

npm install dotenv # Install dotenv to use environment variables in NodeJs

Create .env file with all environment variables and gitignore this file

Create .env.example file with all environment variables and not gitignore this file

 

Configurando ESlint

npm install -D @rocketseat/eslint-config # Install Rocketseat ESLint config

Create .eslintrc.json file with all ESLint config

Create .eslintignore file with all ESLint ignore files

 

TypeScript architecture

npm install -D typescript # Install TypeScript
npm install -D @types/node # Install @types/node to use types in NodeJs
npm install -D tsx # Install tsx to use compiler TypeScript in NodeJs in development mode
npm install -D tsup # Install tsup to use compiler TypeScript in NodeJs in production mode
npm install zod # Install zod to use types in NodeJs and validate data
npx tsc --init # Create tsconfig.json
"target": "es2020", // Change TypeScript target to ES2020 in tsconfig.json
"baseUrl": "./", // Specify the base directory to resolve non-relative module names.
"paths": {
  "@/*": [
    "./src/*"
  ],
} // Specify path aliases to import files in tsconfig.json

 

Fastify architecture

npm install fastify # Install Fastify
npm install @fastify/jwt # Install @fastify/jwt to use JWT in Fastify
npm install @fastify/cookie # Install @fastify/cookie to use cookie in Fastify
npm install @fastify/swagger # Install @fastify/swagger to use Swagger in Fastify
npm install @fastify/swagger-ui # Install @fastify/swagger-ui to use Swagger UI in Fastify

Create fastify-jwt.d.ts file in @types folder with all types of JWT in Fastify

import '@fastify/jwt'

declare module '@fastify/jwt' {
  export interface FastifyJWT {
    user: {
      sub: string
    }
  }
}

Create JWT_SECRET in .env and .env.example

# Auth token in development mode
JWT_SECRET="secret"

Create script fastifyJwt in app.ts to use JWT in Fastify

app.register(fastifyJwt, {
  secret: env.JWT_SECRET,
})

Create openapi.json file to documentation open API 3.0.1, save src/docs

 

Docker architecture

Instalando Docker https://docs.docker.com/get-docker/

# Command to run postgres image database without docker-compose. This image is from bitnami (https://hub.docker.com/r/bitnami/postgresql)
docker run --name api-solid-pg -e POSTGRESQL_USERNAME=docker -e POSTGRESQL_PASSWORD=docker -e POSTGRESQL_DATABASE=apisolid -p 5432:5432 bitnami/postgresql
# We don't use this command because we use docker-compose to run all images, this command is just to show how to run a single image
// Create scripts in package.json
"scripts": {
  "start-docker": "docker-compose up -d", // Create script to run docker-compose in background
  "stop-docker": "docker-compose stop" // Create script to stop docker-compose
},

Create docker-compose.yml file with all docker-compose config

Create .dockerignore file with all docker-compose ignore files

# Commands to use docker
docker ps # List all running containers
docker ps -a # List all containers
docker images # List all images
docker pull mysql # Pull mysql image database (if you want to use mysql)
docker pull postgres # Pull postgres image database (if you want to use postgres)
docker pull mariadb # Pull mariadb image database (if you want to use mariadb)
docker start <container_id or container_name> # <container_id> Start container with id | <container_name> Start container with name
docker stop <container_id or container_name> # <container_id> Stop container with id | <container_name> Stop container with name
docker pause <container_id or container_name> # <container_id> Pause container with id | <container_name> Pause container with name
docker unpause <container_id or container_name> # <container_id> Unpause container with id | <container_name> Unpause container with name
docker rm <container_id or container_name> # <container_id> Remove container with id | <container_name> Remove container with name
docker logs <container_id or container_name> # <container_id> Show logs of container with id | <container_name> Show logs of container with name
docker inspect <container_id or container_name> # <container_id> Show all information of container with id | <container_name> Show all information of container with name
docker-compose --version # Show docker-compose version
docker-compose up # Run docker-compose, show logs in terminal
docker-compose up -d # Run docker-compose in background, not show logs in terminal
docker-compose start # Start o docker-compose
docker-compose stop # Stop o docker-compose, not remove all data in database
docker-compose down # Stop and remove o docker-compose, obs remove all data in database

 

PrismaJs database architecture

npm install prisma # Install Prisma
npm i prisma-erd-generator @mermaid-js/mermaid-cli # Install Prisma ERD generator
npm i @prisma/client # Install Prisma client

npx prisma init # Create prisma folder with prisma.schema and prisma folder
npx prisma migrate dev # Enter a name for the new migration: » created tab Habits
npx prisma studio # Open Prisma Studio in browser
npx prisma studio -b firefox -p 5173 # Open Prisma Studio in browser with specific port and browser
npx prisma generate # Generate diagram in prisma folder
npx prisma db seed # Seed database with data in prisma/seed.ts - Populate database with data for development
// Add generator in prisma.schema
generator erd {
  provider = "prisma-erd-generator"
}
// Create scripts in package.json
"scripts": {
  "studio": "npx prisma studio -b firefox -p 5173", // Create script to open Prisma Studio in browser with specific port and browser
  "generate": "npx prisma generate", // Create script to generate diagram in prisma folder
  "migrate": "npx prisma migrate dev", // Create script to migrate database
  "seed": "npx prisma db seed" // Create script to seed database
},

Environment variables to databases mysql

 

Create DATABASE_URL in .env and .env.example file with MySql

DATABASE_URL="mysql://USER:PASSWORD@HOST:PORT/DATABASE_NAME"

SHADOW_DATABASE_URL="mysql://OTHER_USER:PASSWORD@HOST:PORT/OTHER_DATABASE_NAME"

I used mysql, but you can use postgresql, mariadb, sqlite, sqlserver, mongodb, etc.

// Add datasource in prisma.schema to mysql database
datasource db {
  provider          = "mysql"
  url               = env("DATABASE_URL")
  shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
// https://www.prisma.io/docs/concepts/components/prisma-migrate/shadow-database
// Exist provider that user cannot have access to create a database, to resolve this problem, you can use shadow database

 

Vitest architecture

npm install -D vitest # Install Vitest
npm install -D vite-tsconfig-paths # To vite understand tsconfig paths
npm install -D @vitest/coverage-c8 # Install coverage vitest
npm install -D @vitest/ui # Install vitest ui
npm install -D supertest # Install supertest to test http requests
npm install -D @types/supertest # Install types supertest

Create vite.config.ts file with all vitest config

import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  plugins: [tsconfigPaths()],
})
// now vitest can understand tsconfig paths
// Create scripts in package.json
"scripts": {
  "test:create-prisma-environment": "npm link ./prisma/vitest-environment-prisma", // Create vitest-environment-prisma in node_modules
  "test:install-prisma-environment": "npm link vitest-environment-prisma", // Install vitest-environment-prisma in node_modules
  "test": "vitest run --dir src/use-cases", // Run all tests without watch
  "test:watch": "vitest --dir src/use-cases", // Run all tests with watch
  "pretest:e2e": "run-s test:create-prisma-environment test:install-prisma-environment", // Run before test:e2e, run-s is to run scripts in sequence (npm install -D npm-run-all) 
  "test:e2e": "vitest run --dir src/http", // Run all tests without watch in specific folder
  "test:e2e:watch": "vitest --dir src/http", // Run all tests with watch in specific folder
  "test:coverage": "vitest run --coverage", // Run all tests with coverage
  "test:ui": "vitest --ui", // Run all tests with ui
},

Create vitest-environment-prisma to test environment with prisma

cd prisma/vitest-environment-prisma # Enter in vitest-environment-prisma folder
npm init -y # Create package.json
npm link # Link vitest-environment-prisma to node_modules
cd ../../ # Return to root folder
npm link vitest-environment-prisma # Link vitest-environment-prisma to node_modules

Edit package.json file like this

{
  "name": "vitest-environment-prisma",
  "main": "prisma-test-environment.ts"
}

Create prisma-test-environment.ts in vitest-environment-prisma folder

Edit vite.config.ts file with all vitest config

// Any test inside src/http/controllers will run in environment with prisma/vitest-environment-prisma
test: {
  environmentMatchGlobs: [['src/http/controllers/**', 'prisma']],
},

 

Others libraries

npm install -D npm-run-all # Install npm-run-all to run multiple scripts in parallel or sequential
npm install bcryptjs # Install bcryptjs to encrypt password
npm install -D @types/bcryptjs # Install typescript types for bcryptjs
npm install dayjs # Install dayjs to manipulate date

 

⚙️ Funcionalidades

RF - Requisitos Funcionais

  • Deve ser possível se cadastrar na aplicação;
  • Deve ser possível se autenticar na aplicação;
  • Deve ser possível obter o perfil do usuário logado;
  • Deve ser possível obter o número de check-ins do usuário logado;
  • Deve ser possível o usuário obter seu histórico de check-ins;
  • Deve ser possível o usuário buscar academias próximas a ele (até 10km);
  • Deve ser possível o usuário buscar academias por nome;
  • Deve ser possível o usuário fazer check-in em uma academia;
  • Deve ser possível validar o check-in do usuário em uma academia;
  • Deve ser possível cadastrar uma academia;

RN - Regras de Negócio

  • O usuário não pode se cadastrar com um e-mail já existente;
  • O usuário não pode fazer dois check-ins no mesmo dia;
  • O usuário não pode fazer check-in se não estiver perto (100m) da academia;
  • O check-in só pode ser validado até 20 minutos após criação;
  • O check-in só pode ser validado por usuários administradores;
  • A academia só pode ser cadastrada por usuários administradores;

RNF - Requisitos Não Funcionais

  • A senha do usuário deve ser armazenada com hash;
  • Todas as listas de dados devem ser paginadas com 20 itens por página;
  • O usuário deve ser identificado pelo token JWT;
  • Uso de PostgreSQL em ambiente Dev e MySql em ambiente Prod;
  • Uso de PrismaJs para migrations, queries e diagrama de banco de dados;
  • Uso de Fastify para rotas e middlewares;
  • Uso de Zod para validação de dados de entrada;
  • Uso de SupertestJs para testes de integração;
  • Uso de Tsup para compilar o TypeScript em modo de produção;
  • Uso de Tsx para compilar o TypeScript em modo de desenvolvimento;
  • Uso de Eslint para padronização de código;
  • Uso de Prettier para padronização de código;
  • Uso de Docker para deploy da aplicação e padronização de ambiente de desenvolvimento;
  • Uso de Insonmia para testes de requisições;
  • Uso de Github para versionamento de código;
  • Uso Swagger para documentação da API;

 

🧭 Rodando a aplicação (Modo desenvolvimento)

git clone https://github.com/livioalvarenga/Template-Api-Rest-Node-Docker-Prisma.git # Clone este repositório
cd Template-Api-Rest-Node-Docker-Prisma # Acesse a pasta do projeto no seu terminal/cmd
npm install # Instale as dependências
npm run start-docker # Subir o banco de dados em modo de desenvolvimento na porta 5432
npm run dev # Execute a aplicação em modo de desenvolvimento, a aplicação será aberta na porta:3333 - acesse http://localhost:3333

npm run stop-docker # Parar o banco de dados em modo de desenvolvimento

# Ou

npm run lets-code # Sobe o banco de dados em modo de desenvolvimento e executa a aplicação em modo de desenvolvimento

🧭 Rodando a aplicação (Modo produção)

npm run build # Compilar o TypeScript em modo de produção
npm run start # Iniciar o servidor em modo de produção

🧭 Prisma

npm run studio # Iniciar o Prisma Studio para visualizar o banco de dados
npm run migrate # Criar migrations do banco de dados
npm run seed # Popular o banco de dados com dados de desenvolvimento
npm run generate # Gerar diagrama do banco de dados

🧭 Testes

A aplicação possui testes unitários de integração e testes e2e e uma cobertura de testes de 100%. Para executar os testes, execute os comandos abaixo:

npm run test # Executar os testes unitários de integração
npm run test:watch # Executar os testes unitários de integração com watch
npm run test:e2e # Executar os testes e2e
npm run test:e2e:watch # Executar os testes e2e com watch
npm run test:coverage # Verificar a cobertura de testes
npm run test:ui # Executar os testes de integração com Vitest ui

Além disso, utilizamos o GitHub Actions para executar os testes unitários de integração e e2e e verificar a cobertura de testes a cada push na branch main.

Testando API com openAPI (Swagger)

  • Acesse http://localhost:3333/docs
  • Create User in POST /users
  • Authenticate User in POST /sessions _ Copy the token
  • Click on Authorize button
  • Paste the token in Value input

OpenAPI Swagger

 


 

🚀 Deploy

O deploy foi realizado na plataforma Render.com. Para acessar a rota de documentação (docs) da API, clique no link abaixo:

API-Rest-Node-SOLID

As variáveis de ambiente configuradas incluem:

  • DATABASE_URL
  • SHADOW_DATABASE_URL
  • JWT_SECRET
  • NODE_ENV

O comando para construção (Build command) foi configurado da seguinte forma:

npm install && npx prisma migrate deploy && npx prisma generate && npm run build

O banco de dados utilizado foi o MySQL MariaDB. Foi necessário criar um banco de dados sombra (Shadow), pois o serviço utilizado para criar o banco de dados não permite a criação de um banco de dados para testes. O Prisma utiliza um banco de dados sombra para realizar seus processos de migrações (migrations) e testes.


 

🦸 Autor

Olá, eu sou Livio Alvarenga, Engenheiro de Produção | Dev Back-end e Front-end. Sou aficcionado por tecnologia, programação, processos e planejamento. Uni todas essas paixões em uma só profissão. Dúvidas, sugestões e críticas são super bem vindas. Seguem meus contatos.

 

portfólio livio alvarenga perfil LinkedIn livio alvarenga perfil twitter livio alvarenga perfil Instagram livio alvarenga perfil Facebook livio alvarenga perfil YouTube livio alvarenga

perfil vitrinedev livio alvarenga


 

📝 Licença

Este projeto é MIT licensed.

#CompartilheConhecimento