/Hamburgueria-backend

Usuário pode se cadastrar na aplicação, escolher os pedidos e verificar o carrinho de compras

Primary LanguageJavaScript

Backend da Hamburgueria 🍔🛒


Este foi o projeto que aprendi no curso do DevClub

Usuário pode se cadastrar na aplicação, escolher os pedidos e verificar o carrinho de compras.

Introdução

Aplicação com intenção de simular um sistema de compras em um estabelecimento alimentício. O usuário pode se cadastrar e logar com seus dados de e-mail e senha. Depois de logado e com o token autenticado, a página inicial com os produtos da loja fica visível para escolher variedades de hambúrgueres e complementos. Por fim, pode clicar no carrinho.

Além de um painel administrativo onde o representante do estabelecimento consegue ver todos os pedidos e seus status atualizados.

Tecnologias utilizadas

  • VS Code
  • Insomnia
  • Yarn
  • Node.JS
  • Express.JS
  • Docker
  • PostgreSQL
  • Postbird
  • Sequelize (ORM)
  • UUID (v4)
  • Yup
  • Bcrypt
  • Multer
  • JWT (JSON Web Token)
  • Mongoose
  • MongoDB Compass

Ferramentas

Na aplicação foi utilizado o Node.JS na versão 18.14.0 em LTS.

~

O Yarn na versão 1.22.19

🔸 npm install -g yarn ou sudo npm install -g yarn

~

O Docker na configuração para computador 4.21.1 e versão 24.0.2

~

O Postbird na versão instalada em Postbird.Setup.0.8.4.exe

~

O Sequelize na versão 6.32.1

🔸 yarn add sequelize

~

Foi usado o programa Insomnia para testar as requisições das rotas simulando o Front-end.

~

O MongoDB Compass (GUI) na configuração do computador versão 1.40.4 (Estável).

~

Dependências

Framework Express na versão 4.18.2

🔸 yarn add express

~

Biblioteca UUID do tipo v.4 e na versão 9.0.0

🔸 yarn add uuid

~

Biblioteca Yup na versão 1.2.0

🔸 yarn add yup

~

Biblioteca Bcrypt na versão 5.1.0

🔸 yarn add bcrypt

~

Biblioteca Multer na versão 1.4.5-1ts.1

🔸 yarn add multer

~

Biblioteca JWT na versão 9.0.2

🔸 yarn add jsonwebtoken

~

Biblioteca Mongoose na versão 7.6.1

🔸 yarn add mongoose

~

Dependências de desenvolvimento

O Nodemon na versão 3.0.1

🔸 yarn add nodemon -D

Criado um script no package.json para o nodemon: "dev".

~

O Sucrase na versão 3.33.0

🔸 yarn add sucrase -D

~

O Eslint na versão 8.0.1

🔸 yarn add eslint -D

~

O Prettier nas versões 8.8.0 e 5.0.0

🔸 yarn add prettier eslint-config-prettier eslint-plugin-prettier -D

~

O Sequelize-cli na versão 6.6.1

🔸 yarn add sequelize-cli -D

~

Rodando o projeto

  • Para rodar o repositório é necessário clonar o mesmo.

  • Pra inicar escreva no terminal:

npm init -y
  • Após instaladas as dependências, dê o comando seguinte para rodar a aplicação do servidor:
yarn dev
  • Parar de rodar o servidor: no terminal clicar nas teclas de "Ctrl" e "C".

  • Para debugar:

yarn debug
  • Para usar o Sequelize com o PostgreSQL, precisa instalar:
yarn add pg pg-hstore
  • Para rodar a Migration:
yarn sequelize db:migrate
  • Para desfazer todas as Migrations:
yarn sequelize db:migrate:undo:all
  • Para rodar o banco de dados relacional PostgreSQL:
docker start codeburguer-postgres
  • Para derrubar o banco de dados relacional PostgreSQL:
docker stop codeburguer-postgres
  • Utilizei o Insomnia para realizar as requisições desejadas.

  • Para rodar o banco de dados não relacional MongoDB:

docker start mongo
  • Para derrubar o banco de dados não relacional MongoDB:
docker stop mongo

~

Endpoints

Caminho da URL: http://localhost:3000

Método URL Descrição
POST /users Cria um usuário usando as informações enviadas dentro do arquivo request.body.
POST /sessions Verifica se o e-mail e senha correspondem com as informações do usuário cadastrado no banco de dados.
POST /products Cria um produto usando as informações enviadas dentro do arquivo request.body. E faz o upload do arquivo recebido do Insomnia.
GET /products Procura todos os produtos no banco de dados da tabela products. E retorna um JSON da lista com todos os dados dos produtos.
POST /categories Cria uma categoria usando a informação enviada dentro do arquivo request.body com token. E faz o upload do arquivo recebido do Insomnia.
GET /categories Procura todas as categorias no banco de dados da tabela categories. E retorna um JSON da lista com todos os dados das categorias.
POST /orders Cria um pedido usando as informações enviadas dentro do arquivo request.body com token atualizado.
GET /orders Procura todos os pedidos no banco de dados. Sem o body e com token atualizado. E retorna um JSON da lista com informações de user, products e status.
PUT /orders/:id Atualiza o status específico do pedido, a partir do id, usando o arquivo request.body. E responde com uma mensagem de sucesso.
PUT /products/:id Atualiza algum dado específico do pedido, a partir do id, usando o arquivo request.body. Faz o upload do arquivo recebido do Insomnia (quando tiver). E responde com uma mensagem de sucesso.
PUT /categories/:id Atualiza algum dado específico da categoria, a partir do id, usando o arquivo request.body. Faz o upload do arquivo recebido do Insomnia (quando tiver). E responde com uma mensagem de sucesso.

Middlewares

Serve como interceptador.

Assim que o usuário tentar acessar a rota, faz uma validação da informação se está correta ou se está incorreta.

middlewares() {
    this.app.use(express.json())
    this.app.use('/product-file', express.static(resolve(__dirname, '..', 'uploads')))

    this.app.use('/category-file', express.static(resolve(__dirname, '..', 'uploads')))
  }

~

routes() {
    this.app.use(routes)
  }

~

Usando JWT:
export default(request, response, next) => {
    const authToken = request.headers.authorization
  • Se o usuário não mandar um token:

    • Responde status HTTP com o código 401 (Unauthorized)
    • Retorna o seguinte JSON: { error: 'Token não fornecido' }
  • Se o usuário mandar um token errado:

    • Responde status HTTP com o código 401 (Unauthorized)
    • Retorna o seguinte JSON: { error: 'Token inválido' }
  • Se o usuário mandar um token válido:

    • Retorna o JSON com todas as informações dos produtos da tabela do banco de dados. Acesso autorizado para a rota.

~

Migrations

Serve para criações de tabelas para o banco de dados. E para modificá-las.

Precisa estar, primeiramente, com o Docker rodando. E em seguida, precisa rodar a Migration.

  • Criação da Tabela de Usuários:
    • id
    • name
    • email
    • password_hash
    • admin
    • created_at
    • updated_at

~

  • Criação da Tabela de Produtos:
    • id
    • name
    • price
    • category
    • path
    • created_at
    • updated_at

~

  • Criação da Tabela de Categorias:
    • id
    • name
    • path
    • created_at
    • updated_at

~

  • Deletar campo na tabela:
    • Remover coluna category que está na Tabela de Produtos

~

  • Criar campo na tabela:
    • Adicionar coluna category_id na Tabela de Produtos

~

  • Acrescentar campo na tabela:
    • Adicionar coluna offer na Tabela de Produtos

~

  • Acrescentar campo na tabela:
    • Adicionar coluna path na Tabela de Categorias

~

Controllers

Padrão do objeto que espero receber no Insomnia.

Schema do User para criação do usuários:
name: Yup.string().required(),
email: Yup.string().email().required(),
password: Yup.string().required().min(6),
admin: Yup.boolean(),
  • Se encontrar algum dado inválido:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o(s) motivo(s) do(s) erro(s) no formato JSON: { error: err.errors }
  • Se encontrar um email já existente:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o seguinte JSON: { error: 'E-mail já cadastrado' }
  • Criando com sucesso um usuário:

    • Responde status HTTP com o código 201 (Created)
    • Retorna o seguinte JSON: { id: user.id, name, email, admin }

~

Schema do Session para fazer o login do usuário:
email: Yup.string().email().required(),
password: Yup.string().required(),
  • Se não encontrar o email ou senha do usuário:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o seguinte JSON: { error: 'Email ou senha incorreto' }
  • Encontrando com sucesso email e senha do usuário:

    • Responde status HTTP com o código 200 (OK)
    • Retorna o seguinte JSON: {id: user.id, email, name: user.name, admin: user.admin, token: jwt.sign }

~

Schema do Product para criação de produtos:
name: Yup.string().required(),
price: Yup.number().required(),
category_id: Yup.number().required(),
offer: Yup.boolean(),
  • Se encontrar algum dado inválido:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o(s) motivo(s) do(s) erro(s) no formato JSON: { error: err.errors }
  • Criando com sucesso um produto:

    • Responde status HTTP com o código 200 (OK)
    • Retorna o seguinte JSON: { id, name, price, category_id, path, updated_at, created_at, offer }
  • Encontrando todos os produtos cadastrados:

    • Responde status HTTP com o código 200 (OK)
    • Retorna o seguinte JSON: { url, id, name, price, path, updated_at, created_at, category_id: category: { id, name }, offer }
  • Se encontrar um usuário que não seja admin:

    • Responde status HTTP com o código 401 (Unauthorized)

~

Schema do Category para criação de categorias:
name: Yup.string().required(),
  • Se encontrar algum dado inválido:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o(s) motivo(s) do(s) erro(s) no formato JSON: { error: err.errors }
  • Se encontrar alguma categoria repetida:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o erro no formato JSON: { error: 'Categoria já existe' }
  • Criando com sucesso uma categoria:

    • Responde status HTTP com o código 200 (OK)
    • Retorna o seguinte JSON: { name, id }
  • Se encontrar um usuário que não seja admin:

    • Responde status HTTP com o código 401 (Unauthorized)

~

Schema do Order para criação de pedidos:
products: Yup.array().required().of(
                Yup.object().shape({
                    id: Yup.number().required(),
                    quantity: Yup.number().required(),
                })
            )
  • Se encontrar algum dado inválido:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o(s) motivo(s) do(s) erro(s) no formato JSON: { error: err.errors }
  • Criando com sucesso um pedido:

    • Responde status HTTP com o código 201 (Created)
    • Retorna o seguinte JSON: { id: product.id, name: product.name, price: product.price, category: product.category.name, url: product.url, quantity: request.body.products[productIndex].quantity }
  • Se encontrar um usuário que não seja admin:

    • Responde status HTTP com o código 401 (Unauthorized)

~

Schema do Product para atualizações de produtos:
name: Yup.string(),
price: Yup.number(),
category_id: Yup.number(),
offer: Yup.boolean(),
  • Se encontrar algum dado inválido:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o(s) motivo(s) do(s) erro(s) no formato JSON: { error: err.errors }
  • Alterando com sucesso um produto:

    • Responde status HTTP com o código 200 (OK)
  • Se encontrar um usuário que não seja admin:

    • Responde status HTTP com o código 401 (Unauthorized)
  • Se não encontrar algum produto pelo ID:

    • Responde status HTTP com o código 401 (Unauthorized)
    • Retorna o seguinte JSON: { error: 'Verifique se o ID do seu produto está correto' }

~

Schema do Category para atualizações de categorias:
name: Yup.string(),
  • Se encontrar algum dado inválido:

    • Responde status HTTP com o código 400 (Bad Request)
    • Retorna o(s) motivo(s) do(s) erro(s) no formato JSON: { error: err.errors }
  • Se não encontrar alguma categoria pelo ID:

    • Responde status HTTP com o código 401 (Unauthorized)
    • Retorna o erro no formato JSON: { error: 'Verifique se o ID da sua categoria está correto' }
  • Alterando com sucesso uma categoria:

    • Responde status HTTP com o código 200 (OK)
  • Se encontrar um usuário que não seja admin:

    • Responde status HTTP com o código 401 (Unauthorized)

~

Models

Responsável pela leitura e escrita de dados. Auxiliar na interface da aplicação com o banco de dados.

Método static init no User
super.init({
            name: Sequelize.STRING,
            email: Sequelize.STRING,
            password: Sequelize.VIRTUAL,
            password_hash: Sequelize.STRING,
            admin: Sequelize.BOOLEAN,
        },
        {
            sequelize,
        })

~

Método static init no Product
super.init(
            {
            name: Sequelize.STRING,
            price: Sequelize.INTEGER,
            path: Sequelize.STRING,
            offer: Sequelize.BOOLEAN,
            url: {
                type: Sequelize.VIRTUAL,
                get() {
                    return `http://localhost:3000/product-file/${this.path}`
                },
            },
        },
        {
            sequelize,
        })

~

Método static init no Category
super.init(
            {
            name: Sequelize.STRING,
            path: Sequelize.STRING,
            url: {
                type: Sequelize.VIRTUAL,
                get() {
                    return `http://localhost:3000/category-file/${this.path}`
                },
            },
        },
        {
            sequelize,
        })

~

Método static associate no Product
static associate(models){
        this.belongsTo(models.Category, { 
            foreignKey: 'category_id', 
            as: 'category',
        })
    }

~

Status do projeto

✔️ Aplicação back-end finalizada.