eusouregislima / devburgerAPI2.0

0 stars 0 forks source link

Estruturação do projeto #1

Open eusouregislima opened 6 months ago

eusouregislima commented 6 months ago

Início do projeto

eusouregislima commented 6 months ago
eusouregislima commented 6 months ago

Ajeitando o linter de código npm install eslint -D npm eslint --init

Instalando o prettier npm install prettier eslint-config-prettier eslint-plugin-prettier -D montagem do arquivo .prettierrc.json

eusouregislima commented 6 months ago

MVC

eusouregislima commented 6 months ago

Montando um conteiner docker com o postgres docker run --name devburger-postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres parando o contener - docker stop devburger-postgres

docker ps -> mostra os containeres rodando docker ps --all mostra todos os containeres, independente de estar rodando ou não docker stop nome do container -> para o container docker start nome do conteiner -> roda novamente

Após instalar o Beekeeper Studio, conectamos ao container e criamos um banco de dados chamado devburger

eusouregislima commented 6 months ago

SQL Structured query language => É a linguagem utilizada para lidar com banco de dados relacional. Baseado em tabelas. Com ela é possível modificar e consultar os dados de maneira simplificada.

ORM Object Relational Mapping -> Ele mapeia o banco e e cria uma interface entre a linguagem de programação e o SQL, ajudando na produtividade. O ORM que vamos utilizar é o SEQUELIZE -> Ele vai traduzir para o banco de dados os meus pedidos em JS.

Dentro de SRC -> Pasta app/controllers e app/models Dentro de SRC pasta config com arquivo database.js Dentro de SRC pasta database com index.js e pasta migrations

-> instalar o Sequelize -> npm install sequelize pg pg-hstore Instalando o sequelize-cli como dependência de desenvolvimento para faciliar as operações pela linha de comando: npm install -D sequelize-cli

Criar arquivo na raiz: .sequelizerc com o conteúdo: const { resolve } = require('node:path');

module.exports = { config: resolve(dirname, 'src', 'config', 'database.js'), 'models-path': resolve(dirname, 'src', 'app', 'models'), 'migrations-path': resolve(__dirname, 'src', 'database', 'migrations'), };

e no arquivo database.js que está em config colocar: module.exports = { dialect: 'postgres', host: 'localhost', port: 5432, username: 'postgres', password: 'postgres', database: 'devburger', define: { timestamps: true, underscored: true, underscoredAll: true, }, };

eusouregislima commented 6 months ago

Migrations

Criando a primeira migration yarn sequelize migration:create --name create-users-table Esse comando cria um arquivo dentro da pasta migrations

Mudanças feitas no arquivo criado conforme uso: 'use strict';

/* @type {import('sequelize-cli').Migration} / module.exports = { async up(queryInterface, Sequelize) { await queryInterface.createTable('users', { id: { primaryKey: true, allowNull: false, type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, }, name: { type: Sequelize.STRING, allowNull: false, }, email: { type: Sequelize.STRING, allowNull: false, unique: true, }, password_hash: { type: Sequelize.STRING, allowNull: false, }, admin: { type: Sequelize.BOOLEAN, defaultValue: false, }, created_at: { type: Sequelize.DATE, allowNull: false, }, updated_at: { type: Sequelize.DATE, allowNull: false, }, }); },

async down(queryInterface) { await queryInterface.dropTable('users'); }, };

Rodando a migration yarn sequelize db:migrate

Comando para desfazer a última migration yarn sequelize db:migrate:undo

Comando para desfazer todas as migrations yarn sequelize db:migrate:all

eusouregislima commented 6 months ago

Criando o Model de usuários

class User extends Model { static init(sequelize) { super.init( { name: Sequelize.STRING, email: Sequelize.STRING, password_hash: Sequelize.STRING, admin: Sequelize.BOOLEAN, }, { sequelize, }, ); } }

export default User;

O id, created_at e updated_at não precisaram ser colocados pois são valores padrões.

eusouregislima commented 6 months ago

Configurando o model de usuários

import configDatabase from '../config/database';

import User from '../app/models/User';

const models = [User];

class Database { constructor() { this.init(); }

init() { this.connection = new Sequelize(configDatabase); models.map((model) => model.init(this.connection)); } }

export default new Database();

Aqui importamos o config do database, o model de user, guardei os models em um array para fazer um map depois. O objetivo é que todos os models usem apenas uma conexão.

eusouregislima commented 6 months ago

Criando o primeiro controller, lembrando que é da responsabilidade dele conter as regras de negócio observando os Models também Métodos: /**

Nunca haverá método repetido no mesmo controller

Método criado e respondendo para o front somente o que usará:

import { v4 } from 'uuid';

import User from '../models/User';

class UserController { async store(request, response) { const { name, email, password_hash, admin } = request.body;

const user = await User.create({
  id: v4(),
  name,
  email,
  password_hash,
  admin,
});

return response.status(201).json({
  id: user.id,
  name,
  email,
  admin,
});

} }

export default new UserController();

Arquivo de rotas atualizado:

import { Router } from 'express'; import UserController from './app/controllers/UserController';

const routes = new Router();

routes.post('/users', UserController.store);

export default routes;

Instalamos o HTTPie Em enviroments criamos a BASE_URL como http://localhost:3001 A rota com o método POST foi renomeada para create user e movida para a collection user que também foi criada A rota foi utilizada assim: {{BASE_URL}}/users Enviando pelo body o json que o servidor esperava.

eusouregislima commented 6 months ago

Instalando o YUP npm install yup

Importando tudo do yup de uma vez import * as Yup from 'yup'

Adicionamos essa validação no arquivo e um retorno para os erros.

const schema = Yup.object({ name: Yup.string().required(), email: Yup.string().email().required(), password_hash: Yup.string().min(6).required(), admin: Yup.boolean(), });

try {
  schema.validateSync(request.body, { abortEarly: false });
} catch (err) {
  return response.status(400).json({ error: err.errors });
}
eusouregislima commented 6 months ago

Validando se o usuário já existe com um email cadastrado

const userExists = await User.findOne({ where: { email, }, });

if (userExists) {
  return response.status(400).json({ error: 'User already exists' });
}
eusouregislima commented 6 months ago

Criando o hash de senha Instalamos o bcrypt

E as modificações no Model de user

import Sequelize, { Model } from 'sequelize'; import bcrypt from 'bcrypt';

class User extends Model { static init(sequelize) { super.init( { name: Sequelize.STRING, email: Sequelize.STRING, password: Sequelize.VIRTUAL, password_hash: Sequelize.STRING, admin: Sequelize.BOOLEAN, }, { sequelize, }, );

this.addHook('beforeSave', async (user) => {
  if (user.password) {
    user.password_hash = await bcrypt.hash(user.password, 10);
  }
});

return this;

} }

export default User;

Também trocamos para password os campos que eram password_hash para password no controller.

eusouregislima commented 6 months ago

Criando a parte de autenticação do usuário

eusouregislima commented 6 months ago

Criando a migration de produtos yarn sequelize migration:create --name create-products-table Montagem

yarn sequelize db:migrate -> rodar

eusouregislima commented 6 months ago

Criando o controller de produtos

import * as Yup from 'yup';

class ProductController { async store(request, response) { const schema = Yup.object({ name: Yup.string().required(), price: Yup.number().required(), category: Yup.string().required(), });

try {
  schema.validateSync(request.body, { abortEarly: false });
} catch (err) {
  return response.status(400).json({ error: err.errors });
}

return response.status(201).json({ message: 'ok' });

} }

export default new ProductController();

eusouregislima commented 6 months ago

upload de imagens com o multer Instalar o multer

Criar pasta uploads na raiz

Arquivo multer.js na pasta config import multer from 'multer'; import { v4 } from 'uuid'; import { extname, resolve } from 'node:path';

export default { storage: multer.diskStorage({ destination: resolve(__dirname, '..', '..', 'uploads'), filename: (request, file, callback) => { return callback(null, v4() + extname(file.originalname)); }, }), };

Nas routes

import { Router } from 'express'; import multer from 'multer'; import multerConfig from './config/multer';

import UserController from './app/controllers/UserController'; import SessionController from './app/controllers/SEssionController'; import ProductController from './app/controllers/ProductController';

const routes = new Router();

const upload = multer(multerConfig);

routes.post('/users', UserController.store); routes.post('/session', SessionController.store); routes.post('/products', upload.single('file'), ProductController.store);

export default routes;

eusouregislima commented 6 months ago

Finalizando a parte 1 do backend

No controller

Criamos a rota para poder listar os produtos e recuperar a url do arquivo, Avisamos para o express em um middleware que ele deve servir esse arquivo

Registramos no database a model que criamos.