controllers | ||
images | ||
models | ||
services | ||
utils | ||
.dockerignore | ||
.editorconfig | ||
.env.model | ||
.eslintignore | ||
.eslintrc.json | ||
.gitignore | ||
compose-dev-with-front.yml | ||
compose-dev.yml | ||
compose-with-front.yml | ||
compose.yml | ||
Dockerfile | ||
Dockerfile-dev | ||
download_front.sh | ||
Functional-Requirements.md | ||
index.js | ||
package-lock.json | ||
package.json | ||
README.md |
Boas vindas ao repositório Games Store (Backend)
Esse projeto é Uma API RESTfull de um sistema de gerenciamento de uma loja de games, onde é possível criar, visualizar, deletar e atualizar os produtos e as vendas. Usado Node.js, Express e MongoDB. Ainda em fase de desenvolvimento, porém é possível já utilizar.
Tem FRONT ?
Aqui está o link do repositório frontend, onde desenvolvi algumas interfaces para interagir com esta API.
No entanto, é possível executar esse frontend por aqui também, se seguir a correta opção ("COM FRONT") que deixei na seção Via DOCKER.
Sumário
- Habilidades
- Descrição
- Requisitos de usuário
- Arquitetura e padrões
- Tecnologias utilizadas
- Futuras implementações
- Requisitos para execução da API
- Como executar
- Endpoints
- Linter
- Observações
- Requisitos funcionais
Habilidades
Esse projeto teve como objetivo praticar as seguintes hardskills:
- Estruturação de uma aplicação em camadas (Arquitetura MSC);
- Delegação de responsabilidades específicas para cada parte do app;
- Melhora da manutenibilidade e reusabilidade do código;
- Aplicação dos padrões REST;
- Implementação de uma API intuitiva e facilmente entendível.
Descrição
Se trata de uma API de gerenciamento de vendas que manipula produtos e vendas. Basicamente é um modesto controle de estoque em forma de CRUD para lidar tanto com produtos como com as vendas .
Seguindo os princípios REST foi desenvolvido alguns ENDPOINTS que se conectam a um banco de dados NÃO relacional (NoSQL), MongoDB.
Há uma Collection para produtos (products) serem cadastrados na aplicação, afim de ser possível fazer vendas com esses produtos. Da mesma forma, uma Collection para vendas (sales) também foi criada. Essas vendas são realizadas conforme a quantidade de produtos disponíveis em estoque.
Requisitos de Usuário
- Deve ser possível que a pessoa usuária, independente de cadastramento ou login, possa adicionar, ler, deletar e atualizar produtos no seu estoque. (Mas irei implantar Login e autorizações de usuários também brevemente)
- O usuário deve poder também enviar vendas para o sistema. Essas vendas devem validar se o produto em questão existe.
- Deve, também, ser possível ler, deletar e atualizar vendas.
Arquitetura e Padrões
- Arquitetura MSC
- API RESTfull
Tecnologias utilizadas
- Node.js
- Express
- MongoDB
- ESLinter (Para auxiliar na análise de erros no código e também para ajudar manter algumas boas práticas)
Futuras implementações
- Autenticação de Usuários (Login)
- Autorizações de usuários com uso de tokens (JWT). Parecido com o que fiz no projeto Blog API.
- Implantar a aplicação via Cloud ou VPS, para que possa ser consumida e testada externamente.
- Implantar o Banco de Dados em nuvem.
Requisitos para execução da API
Você pode rodar esse projeto de duas maneiras: 1 - Via Docker 🐳 ou 2 - Manualmente 🖐️
Opção 1 - Via Docker
Se não tiver, baixe-o no site oficial
Opção 2 - Manualmente (Via Host)
-
Node.js
Nodejs é um framework que tem um engine do Chrome modificada o qual permite que exista, no lado servidor, uma aplicação Web desenvolvida em JavaScript. Portanto é o primeiro sofware que permite esse projeto acontecer.
Já testei com Node nas versões 14 à 18. Acredito que funcione nas outras versões. Para instalar , configurar e gerenciar Nodejs de forma produtiva, recomendo uso da ferramenta NVM. Com NVM você consegue, "com um dedo", instalar várias versões do Nodejs e escolher qual usá-las quando precisar. Mas, se quiser ter mais trabalho, baixe o Nodejs do site oficial.
-
MongoDB
Essa API se comunica com um banco de dados não relacional (noSQL) implementado em MongoDB. Então precisa ter em algum lugar (local ou remotamente) o software gerenciador de servidor de banco dados, MongoDB, que se possa usar aqui.
Caso não o tenha, faça download e instale-o.
Site oficial para download do MongoDB Comunnity Edition.
-
Software cliente para requição de API (ex.: Postman, Insomnia e etc)
Para testar todos os métodos de requição à essa API, se faz necessário um Software específico para isso. Dentre os mais conhecidos até o momento são Postman e Insomnia.
Se for apenas testar o método GET no endpoint, apenas com um browser comum é possível.
Por exemplo. Fazendo uma requisição GET a partir de um Browser (Firefox, Chrome e etc) para a URL http://localhost:3001/products , funciona.
Como Executar
Via DOCKER
Criei diferentes MODOS para executar esse software via Docker. Os classifiquei assim: "Normal (frontendless)", "Normal + Modo Dev", "Com Frontend" e "Com Frontend + Modo Dev".
☑️ NORMAL
Esse modo é básico e padrão, sem frontend ("frontendless"); é só a API respondendo requisições e conversando com banco de dados. Trata-se do levantamento de um container que dá acesso a API na porta escolhida (padrão: 3001) e pronto. Com isso, basta verificar o número IP do gateway da rede docker criada e fazer a requisição para o(s) endpoint(s) desejado(s). Mostro como fazer tudo isso mais a frente. NADA DEMAIS. ;-)
A seguir temos os passos de como usar esse modo ("Normal"), de acordo com a introdução que fiz acima. Junto deixei algumas recomendações técnicas que possivelmente você só precisa ler uma vez, já que se aplica aos demais modos também.
========= COMO USAR =========
Para executar o sofware nesse modo, faça as seguintes etapas:
1. Verifique o arquivo compose.yml
Se você for um desenvolvedor, talvez queira mudar algo nesse aquivo para atender às tuas especificidades, como por exemplo 1 - Porta exposta e 2 - Nome da rede . Ahh !.. talvez queira, tmbém, colocar uma outra images nos dockerfiles. Sei lá. Umas imagens mais "levinhas" e tal ..🌩️ . Depois vou trocar também.
- Caso decida trocar o valor da variável DB_NAME no arquivo, penso ser boa prática também trocá-las nos arquivos Dockerfile e models/Dockerfile e VICE-VERSA em prol da legibilidade e documentação. Principalmente se precisar fazer testes ou depurações subindo container, manualmente, sem auxílio do docker compose.
Dito isso, se precisas alterar algo mais nesses arquivos ("compose" e "Dockerfiles"), LEIA, antes, as linhas comentadas dentro dos arquivos para não cometer um "ato falho" que inviabilize a execuçao do software. O valor de uma variável pode está vinculado à uma lógica usada em outro local que ler essa variável. Um exemplo disso são as variáveis de ambientes DB_HOST e PORT do serviço de backend.
Ok! Próximo ...
_2. Execute o docker compose
Estando na pasta do arquivo compose.yml, via CLI, basta digitar:
docker compose up -d
3. Localize o container criado
O nome do container deixei que fosse gerado automaticamente, com estrutura padrão, formado por:
`nome_da_PASTA + nome do SERVIÇO + um NÚMERO`
Então o primeiro container do projeto para o serviço de backend, por exemplo, terá um nome parecido com: games-store-back-backend-1
.
4. Identifique o IP do gateway do container
Como criei numa rede nesse projeto, a documentação docker classifica isso como "user-defined bridge", logo para acessar o container a partir do host, precisamos usar o ip que Docker define para o gateway dessa rede que criamos. Portanto, depois que subimos o(s) container(s), precisamos localizar o número IP do gateway desse container para poder conseguir fazer requisição usando esse IP.
Existem diversas maneiras de fazer isso se estiver usando o Docke via CLI. A que acho mais fácil é inspecionar o próprio container e, com auxílio do grep, filtrar a palavra "Gateway" que já vem acompanhado com seu repspectivo número IP. Veja abaixo:
Sintaxe:
sudo docker container inspect < nome ou id do container > | grep Gateway
5. Faça requisição para um endpoint
De posse do número IP, usando alguma ferramenta de requisição como Postman e Insomnia, faça requisição para um endpoint da API.
Use o IP do Gateway obtido na etapa anterior. Escolha um dos endpoints que listei mais à frente, na seção Endpoints.
Exemplo com endpoint products
seria: <IP DO GATEWAY>:3001/products
☑️ NORMAL + MODO DEV
Esse modo meio que herda todas observações do modo NORMAL citado anteriormente, portanto, não vou reptir quase nada, "apenas fazer algumas referências e acrescentar as diferenças" (poeta, eu ?).
Esse modo consiste em usar outro arquivo de docker compose (compose-dev.yml) o qual se conecta a outro arquivo Dockerfile (Dockerfile-dev) configurados de forma que permita que as alterações de código feitas no host, reflita, em tempo real, dentro do container e vice-versa. Esse modo facilita para quem está desenvolvendo (daí o sobrenome "modo dev") ou fazendo alterações no projeto, pois não precisa ficar fazendo rebuild a cada alteração, nem reiniciar o container, bastando, apenas, fazer uma nova requisição para o endpoint desejado. Lógico que em produção seriaa usado o arquivo compose do modo NORMAL por exemplo.
========= COMO ISSO FUNCIONA =========
Como menionado, o compose-dev.yml usa como contexto de build o arquivo Dockergile-dev. Esse Dockerfile-dev tem nada menos ue um comando diferente no entrypoint, que é o comando npm run dev
, ao invés de npm stat
. Esse comando npm run dev que executa o script que tem nodemon no package.json
no lugar do node.
O nodemon é uma ferremnta que executar script JS mas monitora em tempo real mudanças ocorridas no código e, automaticamente, reinicia o servidor quado qualquer alteração é salva.
No compose-dev.yml também foi adicionado um volume do tipo bind mount. Isso que permite vincular a pasta raiz do projeto entre host e container, sem precisar fazer rebuild toda hora só pra desenvolvimento. As mudanças em qualquer parte do projeto no host, refletem, diretamente, dento do container e VICE-VERSA.
========= COMO USAR =========
Para executar o sofware nesse modo, faça as seguintes etapas (as mesmas do modo NORMAL com "pífias" exceções):
1. Verifique o arquivo compose-dev.yml
Considere todas observações apresetadas no modo NORMAL.➿
Aqui só trocamos o arquivo do docker compose ( que passa a ser compose-dev.yml
)
2. Execute o docker compose
Aqui também só muda um pouco a sintaxe. Como é um arquivo diferente do padrão, tem que usar a flag -f passando o caminho para o arquivo do docker compose que deseja usar. Se esquecer dessa flag , o docker compose assume o arquivo errado (compose.yml) e irá levantar containers do modo NORMAL, ao invés de NORMAL + MODO DEV:
docker compose -f compose-dev.yml up -d
Note: Pra descer container também use a flag -f , hein. 👁️
3. Localize o container criado
Da mesma maneira já explicada no modo NORMAL. ➿
O nome do container deixei ser gerado automaticamente, com estrutura padrão, formado por:
`nome_da_PASTA + nome do SERVIÇO + um NÚMERO`
4. Identifique o IP do gateway do container
Da mesma maneira já explicado no modo NORMAL.➿
Resumindo...
sudo docker network inspect < nome ou id da rede > | grep Gateway
5. Faça requisição para um endpoint
Da mesma maneira já explicado no modo NORMAL.➿
Use o IP do Gateway obtido na etapa anterior. Escolha um dos endpoints que listei mais à frente, na seção Endpoints.
Exemplo com endpoint products
seria: <IP DO GATEWAY>:3001/products
☑️ COM FRONTEND
Esse modo consiste em excutar a API (esse repositório atual em que estamos) juntamente com o frontend (outro repositório mencionado no começo da documentação). O backend continuará executando na porta 3001 do IP do Gateway, e o frontend executará na porta 3000 do IP do Gateway. Mas o frontend consegue ser acessado, TAMBÈM, pelo localhost. Então, se usar a URL localhost:3000, já consegue acessar tudo (front, back e databse) de uma vez śó.
No entanto, há pré-requisitos para se usar esse modo, devido as configurações feitas no arquivo do docker compose. Caso não sejam atendidos, o modo não funionará. Os pré-requisitos são: 1 - Baixar/clonar previamente o repositório de frontend, 2 - O nome da pasta raiz do repositório de frontend precisa ser "games-store-frontend", 3 - A pasta precisa estar na pasta PAI deste projeto aqui (backend) 4 - Garantir todas permisões de excução recursiva. A seguir vou dar a opção de como preencher esses requistos de uma vez só, e de duas maneiras. 😺
========= COMO ATENDER PRÉ-REQUISITOS =========
As duas que acho melhor de conseguir isso são:
A) Usando o git clone e depois um simples comando:
- Clone o repostiório frontend (Link do repo)
- Depois , para evitar problema, dê permissão recursiva para o repositório baixado:
chmod -R 777 games-store-front
Essas duas etapas já deveria, automaticamente, preencher todas as condições preestabelecidas. Mas se por algum motivo não puder usar git, tem a opção dois abaixo.
B) Obter frontend sem git clone, usando meu script shell:
Acho que forma mais fácil e rápida de preencher todos os requisitos sem usar git, é executando script em shell (em padrão POSIX) que criei: download_front.sh
. hehe 🥰
Esse script encontra-se na raíz do projeto. O que ele basicamente faz é baixar, extrair, mover para a pasta certa, renomear e configura as permissões necessárias para o repositório frontend baixado.
Portanto, se for utilizá-lo, não esqueça de o executar com privilêgios elevados (sudo , root e etc) e verifique se na sua saída são emitidos vários "OK" em cada etapa. Se houver alguma marcação de "FAIL" após a execução do script, siga instruções apresentadas e execute o script novamente, se for o caso.
========= COMO USAR =========
Com o repositório frontend baixado em uma pasta acima da que se encontramos, seguimos as mesmas etapas descritas nos modos anteriores. Aqui vai um "resumo do resumo" delas.
São praticamente as mesmas etapas do modo NORMAL com pequenas exceções:
1. Verifique o arquivo compose-with-front.yml
Considere todas observações apresentadas no modo NORMAL. ➿
Só trocamos o arquivo do docker compose, que passa a ser o compose-with-front.yml
.
2. Execute o docker compose
Aqui também só muda um pouco a sintaxe. Como é um arquivo diferente do padrão, tem que usar a flag -f passando o caminho para o arquivo do docker compose que deseja usar. Se esquecer dessa flag , o docker compose assume o arquivo errado (compose.yml) e irá levantar containers do modo NORMAL, ao invés de COM FRONTEND:
docker compose -f compose-with-front.yml up -d
Note: Pra descer container também use a flag -f , hein. 👁️
3. Localize o container criado
Da mesma maneira já explicada no modo NORMAL. ➿
O nome do container deixei ser gerado automaticamente, com estrutura padrão, formado por:
`nome_da_PASTA + nome do SERVIÇO + um NÚMERO`
4. Identifique o IP do gateway do container
Da mesma maneira já explicado no modo NORMAL. ➿
Resumindo...
sudo docker network inspect < nome ou id da rede > | grep Gateway
5. Faça requisição para um endpoint
Da mesma maneira já explicado no modo NORMAL.➿
Use o IP do Gateway obtido na etapa anterior. Escolha um dos endpoints que listei mais à frente, na seção Endpoints.
Exemplo com endpoint products
seria: <IP DO GATEWAY>:3001/products
☑️ COM FRONTEND + MODO DEV
Esse modo herda a mesmas funcionalidades E PRÉ_REQUISITOS do modo COM FRONTEND, mas com adição das características de "modo-dev" já explicadas no modo NORMAL + MODO DEV. Ou seja, você terá containers backend e frontend juntos, mas poderá aplicar alterações a partir do host, e ver mudanças repercurtirem em tempo real devido ao nodemon. Mais detalhes: os mesmos descritos no modo NORMAL + MODO DEV. Bom, qualquer coisa leia novamente o modo NORMAL + MODO DEV ali em cima e modo COM FRONTEND.
========= COMO USAR =========
Com os PRÉ_REQUISITOS atendidos seguimos as mesmas etapas descritas nos modos anteriores. Aqui vai um "resumo do resumo" delas.
São praticamente as mesmas etapas do modo NORMAL com pequenas exceções:
1. Verifique o arquivo compose-dev-with-front.yml
Considere todas observações apresentadas no modo NORMAL. ➿
Só trocamos o arquivo do docker compose (que passa a ser compose-dev-with-front.yml
)
2. Execute o docker compose
Aqui também só muda um pouco a sintaxe. Como é um arquivo diferente do padrão, tem que usar a flag -f passando o caminho para o arquivo do docker compose que deseja usar. Se esquecer dessa flag , o docker compose assume o arquivo errado (compose.yml) e irá levantar containers do modo NORMAL, ao invés de COM FRONTEND + MODO DEV:
docker compose -f compose-dev-with-front.yml up -d
Note: Pra descer container também use a flag -f , hein. 👁️
3. Localize o container criado
Da mesma maneira já explicada no modo NORMAL. ➿
O nome do container deixei ser gerado automaticamente, com estrutura padrão, formado por:
`nome_da_PASTA + nome do SERVIÇO + um NÚMERO`
4. Identifique o IP do gateway do container
Da mesma maneira já explicado no modo NORMAL. ➿
Resumindo...
sudo docker network inspect < nome ou id da rede > | grep Gateway
5. Faça requisição para um endpoint
Da mesma maneira já explicado no modo NORMAL.➿
Use o IP do Gateway obtido na etapa anterior. Escolha um dos endpoints que listei mais à frente, na seção Endpoints.
Exemplo com endpoint products
seria: <IP DO GATEWAY>:3001/products
Manualmente (Via HOST)
Instalado os requisitos e as dependências necessárias, basta seguir as seguintes etapas:
1. Variáveis de ambiente
Existe um arquivo na raiz do projeto chamado .env.model
.
Apesar de já explicado no tópico "Requisitos para execução da API", não custa ratificar aqui.
As configurações do projeto também podem ser alteradas nesse arquivo, adicionando o valor desejado após o sinal de igual (=) em cada variável.
Lógico que não é obrigado essa alteração, desde que não exista problemas com as configurações padrões do projeto.
Se for usar o arquivo, terá que renomea-lo parar .env
.
As variáveis do arquivo .env.model são as seguintes: PORT, DB_NAME, DB_HOST e DB_PORT.
PORT diz respeito a porta em que o servidor irá rodar. Se nada for atribuido a essa variável, o servidor rodará na porta 3001
.
DB_NAME refere-se ao nome do banco de dados que será criado. Se nada for atribuido, o nome do banco será GamesStore
.
DB_HOST refere-se ao local (hostname ou IP) do Banco de Dados. Se nada for atribuído, será considerado localhost
.
DB_PORT poderá receber a porta onde se encontra o Banco de Dados. Se nada for atribuído, será considerado a porta 27017
.
2. Banco de Dados MongoDB Coloque em execução o software servidor de banco de dados MongoDB.
3. Execute o script para importar dados para o banco de dados
Para que essa API retorne dados na rota products, precisa, obviamente, ter dados no banco chamado GamesStore (ou no banco com nome que você definiu na variável DB_NAME no arquivo .env) na coleção ("tabela") products. Por isso criei um shell script models/db-import-for-host.sh
para automatizar a importação de dados, o qual cria o banco, a collection e importa dados que estão em outro script (arquivo models/dataTestForDb.js
) para o banco criado. Assim, quando requisitado com GET no ENDPOINT products será retornado esses dados previamente importados no banco, isentando a necessidade de criá-los manualmente para testar a API. Se não quiser usar esse script, ok, a API funciona, mas sem retornar dados; a menos que, alternativamente, cadastre-os, manualmente, usando a rota de POST, por exemplo.
Note: Não criei script (importador de dados) para a rota sales. Se requisitares algo para esse ENDPOINT, sem ter dados na collection sales no banco, não terás retorno de dados.
Note 2: Os dados que se encontram no arquivo models/dataTestForDb.js
, foram obtidos do ENDPOINT de produtos para gamers, de uma API pública do Mercado Livre. Apenas modifiquei alguns pontos e parâmetros para se adaptar ao meu projeto.
4. Instale as dependências
Estando com acesso a internet e dentro da pasta raíz do projeto, execute o comando:
npm install
5. Execute o projeto
Estando com acesso a internet e dentro da pasta raíz do projeto, execute o comando:
npm start
Se tudo ocorreu bem, após executar esse comando o servidor da API do projeto estará disponível para uso. Faça as requisições.
6. Fazendo requisições
Use algum software cliente de API (ex.: Postman, Insomnia e etc) e faça as requisições para os ENDPOINTs usando a URL http://localhost:3001
Se for o caso, não esqueça de substituir porta 3001
pela porta que você definiu na variável PORT do arquivo .env
.
Endpoints
Produtos
- GET: /products
- GET: /products/:id
- POST: /products
- PUT: /products/:id
- DELETE: /products/:id
Vendas
- GET: /sales
- GET: /sales/:id
- POST: /sales
- PUT: /sales/:id
- DELETE: /sales/:id
Linter
Foi usado ESLint para fazer a análise estática do código, afim de manter padrões de indentação e espaçamento dos trechos de códigos.
No entanto, para usá-lo de forma mais manual, o projeto já vem com as dependências relacionadas ao ESLint configuradas no arquivos package.json
.
Para executá-lo basta usar --- dentro da pasta do projeto --- o comando npm run lint
. Se a análise do ESLint
encontrar problemas no código, tais problemas serão mostrados no seu terminal. Se não houver problema no seu código, nada será impresso no seu terminal.
ESLint de forma automática
Particulamente eu preferi utilizar o plugin ESLint
pela extensão do VSCode
, em favor da produtividade.
Observações
Estrutura da lista de requisitos funcionais
Estrutura do banco de dados:
-
Observação: O
_id
é gerado automaticamente. -
O banco deve ter duas
collections
: uma para os produtos e outra para as vendas-
A tabela de produtos terá o seguinte nome:
products
-
Os campos da collection
products
terão esse formato:{ "name": "Sony Playstation 5 825gb Digital Edition Cor Branco E Preto", "price": 6499, "thumbnail": "http://http2.mlstatic.com/D_799755-MLA47058389754_082021-I.jpg", "description": "", "quantity": 7 }
-
-
A tabela de vendas terá o seguinte nome:
sales
-
Os campos da tabela
sales
terão esse formato:{ "itensSold": [{ "productId": "5f43cbf4c45ff5104986e81d", "quantity": 2 }] }
-
-
Shape de retorno após "insert" no banco:
-
A tabela de produtos
-
A resposta retornada após um
insert
(criação) no banco precisa ter o seguinte shape:{ "_id": ObjectId("5f43cbf4c45ff5104986e81d"), "name": "Produto tal", "quantity": 10 }
-
-
A tabela de vendas
-
A resposta retornada após um
insert
(criação) no banco precisa ter o seguinte shape:{ "_id": ObjectId("5f43cc53c45ff5104986e81e"), "itensSold": [{ "productId": "5f43cbf4c45ff5104986e81d", "quantity": 2 }] }
-
Requisitos funcionais
Para entender melhor a lógica por trás do código de desenvolvimento da API, é interessante olhar a Requisitos funcionais do projeto. Acesse aqui o readme desses Requisitos Funcionais.