Uma loja de itens medievais, no formato de uma API, utilizando Typescript, NodeJS, Express, JWT, JOI, MySQL.
Aplicação em camadas (Models, Service e Controllers), por meio dessa aplicação, será possível realizar as operações básicas que se pode fazer em um determinado banco de dados: Criação, Leitura, Atualização e Exclusão (ou CRUD, para as pessoas mais íntimas stuck_out_tongue_winking_eye - Create, Read, Update e Delete).
CRUD em um banco de dados, utilizando o MySQL.
🐳 Rodando no Docker vs Localmente
Rode os serviços
nodeedbcom o comandodocker-compose up -d.
- Lembre-se de parar o
mysqlse estiver usando localmente na porta padrão (3306), ou adapte, caso queria fazer uso da aplicação em containers - Esses serviços irão inicializar um container chamado
trybesmithe outro chamadotrybesmith_db. - A partir daqui você pode rodar o container
trybesmithvia CLI ou abri-lo no VS Code.
Use o comando
docker exec -it trybesmith bash.
- Ele te dará acesso ao terminal interativo do container criado pelo compose, que está rodando em segundo plano.
Instale as dependências com
npm install
⚠ Atenção ⚠ Caso opte por utilizar o Docker, TODOS os comandos disponíveis no package.json (npm start, npm test, npm run dev, ...) devem ser executados DENTRO do container, ou seja, no terminal que aparece após a execução do comando docker exec citado acima.
Instale as dependências com
npm install
✨ Dica: Para rodar o projeto desta forma, obrigatoriamente você deve ter o node instalado em seu computador.
🏦 Conexão com o Banco
#### DATABASE VARS
MYSQL_HOST=localhost
MYSQL_USER=root
MYSQL_PASSWORD=password
#### SECRECT VARS
JWT_SECRET=SecretKey
connection.ts e esteja no diretório src/models
🪑 Tabelas
O banco terá três tabelas: pessoas usuárias, produtos e pedidos.
DROP SCHEMA IF EXISTS Trybesmith;
CREATE SCHEMA Trybesmith;
CREATE TABLE Trybesmith.Users (
id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL,
username TEXT NOT NULL,
classe TEXT NOT NULL,
level INTEGER NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE Trybesmith.Orders (
id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL,
userId INTEGER,
FOREIGN KEY (userId) REFERENCES Trybesmith.Users (id)
);
CREATE TABLE Trybesmith.Products (
id INTEGER AUTO_INCREMENT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
amount TEXT NOT NULL,
orderId INTEGER,
FOREIGN KEY (orderId) REFERENCES Trybesmith.Orders (id)
);- O endpoint é acessível através do caminho (
/products);- O resultado retornado para listar produtos com sucesso é conforme exibido abaixo, com um status http
200:[ { "id": 1, "name": "Poção de cura", "amount": "20 gold", "orderId": null }, { "id": 2, "name": "Escudo do Herói", "amount": "100 diamond", "orderId": 1 } ]
- O resultado retornado para listar produtos com sucesso é conforme exibido abaixo, com um status http
-
O endpoint é acessível através do caminho (
/products); -
Os produtos enviados sãp salvos na tabela
Productsdo banco de dados; -
O endpoint recebe a seguinte estrutura:
{
"name": "Espada longa",
"amount": "30 peças de ouro"
}Além disso, as seguintes verificações serão feitas:
👉 Para name
-
[O campo "name" é obrigatório]
- Se o campo "name" não for informado, o resultado retornado deverá ser um status http
400e
{ "message": "\"name\" is required" } - Se o campo "name" não for informado, o resultado retornado deverá ser um status http
-
[O campo "name" deve se do tipo string]
- Se o campo "name" não for do tipo
string, o resultado retornado deverá ser um status http422e
{ "message": "\"name\" must be a string" } - Se o campo "name" não for do tipo
-
[O campo "name" é uma string com mais de 2 caracteres]
- Se o campo "name" não for uma string com mais de 2 caracteres, o resultado retornado deverá ser um status http
422e
{ "message": "\"name\" length must be at least 3 characters long" } - Se o campo "name" não for uma string com mais de 2 caracteres, o resultado retornado deverá ser um status http
👉 Para amount
-
[O campo "amount" é obrigatório]
- Se o campo "amount" não for informado, o resultado retornado deverá ser um status http
400e
{ "message": "\"amount\" is required" } - Se o campo "amount" não for informado, o resultado retornado deverá ser um status http
-
[O campo "amount" deve ser do tipo string]
- Se o campo "amount" não for do tipo
string, o resultado retornado deverá ser um status http422e
{ "message": "\"amount\" must be a string" } - Se o campo "amount" não for do tipo
-
[O campo "amount" é uma string com mais de 2 caracteres]
- Se o campo "amount" não for uma string com mais de 2 caracteres, o resultado retornado deverá ser um status http
422e
{ "message": "\"amount\" length must be at least 3 characters long" } - Se o campo "amount" não for uma string com mais de 2 caracteres, o resultado retornado deverá ser um status http
👉 Para caso os dados sejam enviados corretamente
- [Será cadastrado um produto com sucesso]
- O resultado retornado para cadastrar o produto com sucesso é exibido conforme abaixo, com um status http
201:
{ "id": 1, "name": "Poção de cura", "amount": "20 gold", } - O resultado retornado para cadastrar o produto com sucesso é exibido conforme abaixo, com um status http
-
O endpoint é acessível através do caminho (
/users); -
As informações de pessoas usuárias cadastradas são salvas na tabela
Usersdo banco de dados; -
O endpoint deve receber a seguinte estrutura:
{
"username": "string",
"classe": "string",
"level": 1,
"password": "string"
}Além disso, as seguintes verificações serão feitas:
👉 Para username
-
[O campo "username" é obrigatório]
- Se a requisição não tiver o campo "username", o resultado retornado deverá ser um status http
400e
{ "message": "\"username\" is required" } - Se a requisição não tiver o campo "username", o resultado retornado deverá ser um status http
-
[O campo "username" deve ser do tipo string]
- Se o campo "username" não for do tipo
string, o resultado retornado deverá ser um status http422e
{ "message": "\"username\" must be a string" } - Se o campo "username" não for do tipo
-
[O campo "username" é uma string com mais de 2 caracteres]
- Se o campo "username" não for do tipo
stringcom mais de 2 caracteres, o resultado retornado deverá ser um status http422e
{ "message": "\"username\" length must be at least 3 characters long" } - Se o campo "username" não for do tipo
👉 Para classe
-
[O campo "classe" é obrigatório]
- Se a requisição não tiver o campo "classe", o resultado retornado deverá ser um status http
400e
{ "message": "\"classe\" is required" } - Se a requisição não tiver o campo "classe", o resultado retornado deverá ser um status http
-
[O campo "classe" deve ser do tipo string]
- Se o campo "classe" não for do tipo
string, o resultado retornado deverá ser um status http422e
{ "message": "\"classe\" must be a string" } - Se o campo "classe" não for do tipo
-
[O campo "classe" é uma string com mais de 2 caracteres]
- Se o campo "classe" não for do tipo
stringcom mais de 2 caracteres, o resultado retornado deverá ser um status http422e
{ "message": "\"classe\" length must be at least 3 characters long" } - Se o campo "classe" não for do tipo
👉 Para level
-
[O campo "level" é obrigatório]
- Se a pessoa usuária não tiver o campo "level", o resultado retornado deverá ser um status http
400e
{ "message": "\"level\" is required" } - Se a pessoa usuária não tiver o campo "level", o resultado retornado deverá ser um status http
-
[O campo "level" tem o tipo number]
- Se o campo "level" não for do tipo
number, o resultado retornado deverá ser um status http422e
{ "message": "\"level\" must be a number" } - Se o campo "level" não for do tipo
-
[O campo "level" deve ser um número maior que 0]
- Se o campo "level" não for do tipo
numbermaior que 0, o resultado retornado deverá ser um status http422e
{ "message": "\"level\" must be greater than or equal to 1" } - Se o campo "level" não for do tipo
👉 Para password
-
[O campo "password" é obrigatório]
- Se a requisição não tiver o campo "password", o resultado retornado deverá ser um status http
400e
{ "message": "\"password\" is required" } - Se a requisição não tiver o campo "password", o resultado retornado deverá ser um status http
-
[O campo "password" tem o tipo string]
- Se o campo "password" não for do tipo
string, o resultado retornado deverá ser um status http422e
{ "message": "\"password\" must be a string" } - Se o campo "password" não for do tipo
-
[O campo "password" é uma string com 8 ou mais caracteres]
- Se o campo "password" não for do tipo
stringcom mais de 8 caracteres, o resultado retornado deverá ser um status http422e
{ "message": "\"password\" length must be at least 8 characters long" } - Se o campo "password" não for do tipo
👉 Para caso os dados sejam enviados corretamente
- [A pessoa usuária será cadastrada com sucesso]
- Se a pessoa usuária for cadastrada com sucesso, o resultado deverá ser conforme o exibido abaixo, com um status http
201e retornando um token:
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" } - Se a pessoa usuária for cadastrada com sucesso, o resultado deverá ser conforme o exibido abaixo, com um status http
- O endpoint é acessível através do caminho (
/orders). - Essa rota retorna todos os pedidos e os
ids dos produtos associados a estes.
Além disso, as seguintes verificações serão feitas:
👉 Para orders
- [Listar todos os pedidos com sucesso]
- Quando houver mais de um pedido, o resultado retornado para listar pedidos com sucesso deverá ser conforme exibido abaixo, com um status http
200:
[ { "id": 1, "userId": 2, "productsIds": [1, 2] }, { "id": 2, "userId": 2, "productsIds": [3, 4] } ] - Quando houver mais de um pedido, o resultado retornado para listar pedidos com sucesso deverá ser conforme exibido abaixo, com um status http
-
O endpoint é acessível através do caminho (
/login). -
A rota deve receber os campos
usernameepassword, e esses campos devem ser validados no banco de dados. -
Um token
JWTé gerado e retornado caso haja sucesso no login. -
O endpoint deve receber a seguinte estrutura:
{
"username": "string",
"password": "string"
}Além disso, as seguintes verificações serão feitas:
👉 Para caso haja problemas no login
-
[É validado que o campo "username" é enviado]
- Se o login não tiver o campo "username", o resultado retornado deverá ser um status http
400e
{ "message": "\"username\" is required" } - Se o login não tiver o campo "username", o resultado retornado deverá ser um status http
-
[É validado que o campo "password" é enviado]
- Se o login não tiver o campo "password", o resultado retornado deverá ser um status http
400
{ "message": "\"password\" is required" } - Se o login não tiver o campo "password", o resultado retornado deverá ser um status http
-
[É validado que não é possível fazer login com um username inválido]
- Se o login tiver o username inválido, o resultado retornado deverá ser um status http
401e
{ "message": "Username or password invalid" } - Se o login tiver o username inválido, o resultado retornado deverá ser um status http
-
[É validado que não é possível fazer login com uma senha inválida]
- Se o login tiver a senha inválida, o resultado retornado deverá ser um status http
401e
{ "message": "Username or password invalid" } - Se o login tiver a senha inválida, o resultado retornado deverá ser um status http
👉 Para caso os dados sejam enviados corretamente
- [É possível fazer login com sucesso]
- Se o login foi feito com sucesso, o resultado deverá ser um status http
200e deverá retornar um token:
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" } - Se o login foi feito com sucesso, o resultado deverá ser um status http
-
O endpoint é acessível através do caminho (
/orders); -
Um pedido só pode ser criado caso a pessoa usuária esteja logada e o token
JWTvalidado; -
Os pedidos enviados são salvos na tabela
Ordersdo banco de dados, salvandoidda pessoa usuária da aplicação que fez esse pedido. -
A tabela
Productstambém é alterada, atualizando todos os produtos com osidincluídos na chaveproductsIdsda requisição, e adicionando nesses produtos oorderIddo pedido recém criado; -
O endpoint deve receber a seguinte estrutura:
{
"productsIds": [1, 2]
}Além disso, as seguintes verificações serão feitas:
👉 Para token
-
[Não é possível cadastrar pedidos sem token]
- Se o token não for informado, o resultado retornado deverá ser um status http
401e
{ "message": "Token not found" } - Se o token não for informado, o resultado retornado deverá ser um status http
-
[Não é possível cadastrar um pedido com token inválido]
- Se o token informado não for válido, o resultado retornado deverá ser um status http
401e
{ "message": "Invalid token" } - Se o token informado não for válido, o resultado retornado deverá ser um status http
👉 Para products
-
[O campo "productsIds" é obrigatório]
- Se o corpo da requisição não possuir o campo "productsIds", o resultado retornado deverá ser um status http
400e
{ "message": "\"productsIds\" is required" } - Se o corpo da requisição não possuir o campo "productsIds", o resultado retornado deverá ser um status http
-
[Não é possível criar um pedido com o campo "productsIds" não sendo um array]
- Se o valor do campo "productsIds" não for um array, o resultado retornado deverá ser um status http
422e
{ "message": "\"productsIds\" must be an array" } - Se o valor do campo "productsIds" não for um array, o resultado retornado deverá ser um status http
-
[Não é possível cadastrar um pedido se o campo "productsIds" for um array vazio]
- Se o campo "productsIds" possuir um array vazio, o resultado retornado deverá ser um status http
422e
{ "message": "\"productsIds\" must include only numbers" } - Se o campo "productsIds" possuir um array vazio, o resultado retornado deverá ser um status http
👉 Para caso os dados sejam enviados corretamente
-
[Um pedido é criado com sucesso com 1 item]
- O resultado retornado para cadastrar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http
201:
{ "userId": 1, "productsIds": [1], } - O resultado retornado para cadastrar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http
-
[SUm pedido é criado com sucesso com vários itens]
- O resultado retornado para cadastrar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http
201:
{ "userId": 1, "productsIds": [1, 2] } - O resultado retornado para cadastrar um pedido com sucesso deverá ser conforme exibido abaixo, com um status http