Open eusouregislima opened 6 months ago
npm install nodemon -D
{ "execMap": { "js": "node -r sucrase/register" } }
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
MVC
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
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, }, };
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
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.
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.
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.
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 });
}
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' });
}
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.
Criando a parte de autenticação do usuário
Criando a migration de produtos yarn sequelize migration:create --name create-products-table Montagem
yarn sequelize db:migrate -> rodar
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();
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;
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.
Início do projeto
[x]
npm init -y
Comando para iniciar um package.jsonPreencher a descrição, no autor um objeto com nome e url
[x] Montar o .gitignore
[x] Criar arquivos
app.js
,routes.js
eserver.js
[x] Instalar o express
[x] No
app.js
importar o express e o routes, instanciar a classe App. Utilizar o método contructor() para com o this colocarthis.app = express()
, chamar os middlewares e o routes com o this também.[x] Adicionar o middleares() com
this.app.use(express.json())
, e o routes comthis.app.use(routes)
.[x] Exportar instanciando o App().app
[x] No arquivo server, importar o app, e criar um método
app.listen(3001, () => console.log("Server is running at port 3001"))
[x] E finalmente, criar uma rota get. Primeiro importar o
{Router}
do express. Instanciar o Router() em uma const chamada routes e criar uma rota get que responde com status 200 e a message Hello Node. Exportar oroutes
.