# 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](https://github.com/becauro/games-store-front) 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](#via-docker). ## Sumário - [Habilidades](#habilidades) - [Descrição](#descrição) - [Requisitos de usuário](#requisitos-de-usuário) - Arquitetura e padrões - Tecnologias utilizadas - Futuras implementações - Requisitos para execução da API - [Como executar](#como-executar) - [Via DOCKER](#via-docker) - [Manualmente](#manualmente-via-host) - [Endpoints](#endpoints) - [Linter](#linter) - [Observações](#observações) - [Requisitos funcionais](#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 Sumário * Arquitetura MSC * API RESTfull ## Tecnologias utilizadas Sumário * 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 Sumário * 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](https://github.com/becauro/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 Sumário 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](https://docs.docker.com/engine/install/) ### Opção 2 - Manualmente (Via Host) 1. **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](https://github.com/nvm-sh/nvm#intro). 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](https://nodejs.org/en/). 2. **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](https://www.mongodb.com/try/download/community) para download do _*MongoDB Comunnity Edition*_. 3. **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](https://www.postman.com/downloads) e [Insomnia](https://insomnia.rest/download). 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 Sumário ### 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: ~~~shell 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: ~~~shell 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](https://www.postman.com/downloads) e [Insomnia](https://insomnia.rest/download), 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](#endpoints). Exemplo com endpoint `products` seria: `: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: ~~~shell 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... ~~~shell 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](#endpoints). Exemplo com endpoint `products` seria: `: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](https://github.com/becauro/games-store-front)) * 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: ~~~shell 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... ~~~shell 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](#endpoints). Exemplo com endpoint `products` seria: `: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: ~~~shell 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... ~~~shell 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](#endpoints). Exemplo com endpoint `products` seria: `: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](https://api.mercadolibre.com/sites/MLB/search?category=MLB1144). 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 Sumário ### 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 Sumário Foi usado [ESLint](https://eslint.org/) 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`](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) pela extensão do `VSCode`, em favor da produtividade. --- ## Observações Sumário ### 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: ```json { "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: ```json { "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: ```json { "_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: ```json { "_id": ObjectId("5f43cc53c45ff5104986e81e"), "itensSold": [{ "productId": "5f43cbf4c45ff5104986e81d", "quantity": 2 }] } ``` ## Requisitos funcionais Sumário 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](Functional-Requirements.md) o **readme** desses _Requisitos Funcionais_.