Introdução
Nesse artigo vou apresentar como inicia um projeto em Node.js com Sequelize, Express e Banco de Dados Postgres.
Node.js
O Node.js pode ser definido como um ambiente de execução Javascript server-side. Isso significa que com o Node.js é possível criar aplicações Javascript para rodar como uma aplicação standalone em uma máquina. Apesar de recente, o Node.js já é utilizado por grandes empresas no mercado de tecnologia, com Netfilx, Uber e LinkedIn.
Sequelize
O Sequelize é um ORM baseado em Promise para Node.js, que suporta os dialetos PostgreSQL,MariaDB, MySQL, MSSQL e SQLite. E recursos a transação, relacionamentos, replicação de leitura e muito mais.
Ele possui um mecanismo de migração muito poderoso que pode transformar um esquema existente de banco de dados em uma nova versão, e também fornece mecanismos de sincronização de DB que podem criar estrutura de banco de dados especificando a estrutura do modelo.
Iniciando o projeto
Vamos começar a criar o projeto em Node.js, primeiro você tem que ter algum banco de dados instalado na sua máquina ou usar algum banco on-line, como Heroku ou elepahnt.sql para usar com o Sequelize. Depois de ter instalado, você cria a pasta com o nome que desejar não é relevante, em seguida, entre na pasta que você criou e execute esse comando(via terminal):
npm init
Com esse comando, você dará procedimento à inicialização do seu projeto. Esse comando funciona como uma ferramenta para criar o arquivo package. json de um projeto. Dentro dela vamos fazer instalações de algumas dependências.
Instalação
Instalando as dependências do projeto.
npm install sequelize express pg bcrypt
npm install -D sequelize-cli
Dependências | README |
---|---|
express | O Express. js é um Framework rápido e um dos mais utilizados em conjunto com o Node.js obs:(use express a partir da versão 4.16.0) |
pg | Cliente PostgreSQL sem bloqueio para Node.js |
bcrypt | bcrypt é um método de criptografia do tipo hash para senhas baseado no Blowfish. |
Com as dependências instaladas, vamos criar uma pasta chamada src vamos usar o padrão MVC. Dentro da pasta src crie um arquivo chamado index.js, nele ficará as configurações do projeto é o arquivo principal do projeto. Em seguida, crie as seguintes pastas:
- router ficará os arquivos de rotas da api;
- controller ficará os arquivos da regra de negócio;
- db ficará as pastas migrations e seeders
//exportando as dependências instaladas e configurando as
const express = require("express");
const router = require('./router');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/", router);
module.exports = app;
Na pasta router crie um arquivo index.js
//nesse arquivos terá a configuração das rotas do projeto.
const { Router } = require("express");
const router = Router();
router.get('/', async (req, res) => {
res.send('api running!');
});
module.exports = router;
Na raiz do projeto crie um arquivo chamado de app.js. Nesse arquivo estou iniciando o projeto na porta 3000, a constante app esta recebendo a importação dos arquivos src/index.js, quando queremos importar um arquivo index.js não precisamos escreve-lo no diretório do require('./src/index.js'), pois, por padrão ele já vai buscar esse arquivo index.js, então não precisamos colocar o nome index.js.
const app = require('./src');
app.listen(3000, () => console.info('app runnning on port 3000'));
No terminal execute esse comando:
node app.js
A aplicação será iniciada. No navegador ou em uma ferramenta que dá suporte à documentação das requisições feitas pela API. Acesse o endereço http://localhost:3000 caso esteja tudo correto, irá exibir essa mensagem na tela api running!
Configurando o Sequelize
Primeiro vamos iniciar o Sequelize com esse comando:
npx sequelize init
Com esse comando, o Sequelize cria alguns arquivos no projeto, como a pasta config, migrations, models e seeders. O projeto ficará assim:
//config
//migrations
//models
//node_modules
//seeders
//src
////controller
////db
////router
////index.js
//app.js
//package.json
Altere para:
//node_modules
//src
////config
////controller
////db
//////migrations
//////seeders
////models
////router
////index.js
//app.js
//package.json
Agora vamos fazer algumas alterações, primeiro vamos à pasta config, nela contém um arquivo chamado de config.json renomei para config.js, e crie outro arquivo chamado de database.js.
à pasta config ficará assim:
//config
////config.js
////database.js
O arquivo config.js altere seu conteúdo para:
module.exports = {
username: "",
password: "",
database: "",
host: "",
port: ,
dialect: "postgres",
define: {
timestamps:true
}
};
informe as credências para a conexão do banco de dados.
O arquivo database.js adicione esse código:
const { Sequelize } = require("sequelize");
const config = require('./config');
const sequelize = new Sequelize(
config.database,
config.username,
config.password,
{
dialect: "postgres",
port: config.port,
},
config.define
);
module.exports = sequelize;
Vamos configurar os models da aplicação, os models são a representação das tabelas do banco de dados em forma de classe, pois, assim podemos manipulá-las mais facilmente através do código. No arquivo models/index.js que é responsável por importar os outros models da aplicação. Altere o models/indes.js para:
"use strict";
const fs = require("fs");
const path = require("path");
const { Sequelize } = require("sequelize");
const basename = path.basename(__filename);
const sequelize = require("../config/database.js");
const db = {};
fs.readdirSync(__dirname)
.filter((file) => {
return (
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
);
})
.forEach((file) => {
const model = require(path.join(__dirname, file))(
sequelize,
Sequelize.DataTypes
);
db[model.name] = model;
});
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
O próximo passo é configurar o Sequelize para encontrar os arquivos nas pastas pra onde os movemos, para isso na raiz do projeto crie um arquivo .sequelizerc e insere nele esse conteúdo obs:(o arquivo será formatado com a extensão Plain Text no vscode altere para Js):
const path = require('path');
module.exports = {
'config': path.resolve(__dirname, 'src', 'config', 'config.js'),
'models-path': path.resolve(__dirname, 'src','models'),
'seeders-path': path.resolve(__dirname, 'src', 'db', 'seeders'),
'migrations-path': path.resolve(__dirname, 'src', 'db', 'migrations')
};
O próximmo passo é alterar o src/index.js para:
const express = require("express");
const database = require('./config/database');
const router = require('./router');
const app = express();
const configureExpress = () => {
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/", router);
return app;
};
module.exports = database.authenticate().then(configureExpress);
Com essa alteração, a aplicação só rodará caso a conexão com o bando de dados tenha ocorrido com sucesso.
Altere app.js para:
const setApp = require("./src");
setApp
.then((app) =>
app.listen(3000, () => console.info("app running on port 3000"))
)
.catch((error) => {
console.error(error);
process.exit(1);
});
O projeto está devidamente configurado, agora precisamos criar os models, migrations, controllers e as rotas. Primeiro, vamos gerar as migrations e model a base dados vai ser duas tabelas uma de account e a do usuário e haverá um relacionamento de um para um, no caso o usuário possui uma conta, então vamos gerar primeiro um migrations e model chamada de account e em seguida a do user, vamos para o código:
comando para gera um model
npx sequelize-cli model:generate --name Account --attributes name:string,planLevel:string
Model gerado:
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Account extends Model {
static associate(models) {
}
};
Account.init({
name: DataTypes.STRING,
planLevel: DataTypes.STRING
}, {
sequelize,
modelName: 'Account',
});
return Account;
};
Vemos que não gerei o id, pois, ele é auto-incremento, então não precisamos dele aqui no model, ótimo pra quem for usar um id com número, mas pra quem for usar com uuid tem que realizar algumas alterações no model, pois, o Postgres com Sequelize não gera automaticamente para isso vamos fazer essas seguintes alterações :
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Account extends Model {
static associate(models) {}
};
Account.init({
id: {
type: DataTypes.UUIDV4,
autoIncrement: true,
primaryKey: true,
defaultValue: DataTypes.UUIDV1,
},
name: DataTypes.STRING,
planLevel: DataTypes.STRING
}, {
sequelize,
modelName: 'Account',
});
return Account;
};
Vamos fazer o mesmo com o model do User e em relação com o uuid é a mesma forma no model do User, execute esse comando:
npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string,password:string
Pronto, os devidos models já foram criados, e automaticamente as migrations já foram criadas, e agora precisamos alterar o id do migrations para uuid e adicionar um column no user realizando um relacionamento com account o nome do campo account_id.
Migrations
No banco de dados relacional é uma coleção de dados que já tem uma estrutura de relacionamento predefinidos com uma organização de linhas e colunas. Vamos imaginar que estamos trabalhando em uma equipe com um banco de dados relacional e por algum motivo mais de uma pessoa está utilizando a mesma tabela. Para adequar a uma necessidade que surgiu um deles precisa que o telefone do usuário na tabela seja representado como um inteiro e não uma string, então ele dá um push em desenvolvimento e quebra a atividade do companheiro de equipe.
Percebe o quanto isso pode quebrar a produtividade quando falamos em trabalho em equipe? É exatamente por isso que as migrations foram criadas para ser um controle de versionamento de um estado para outro dos bancos de dados, assim como o GIT é para código da aplicação.
Migrations
migrations do acccount:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Accounts', {
id: {
allowNull: false,
primaryKey: true,
default: Sequelize.UUID,
type: Sequelize.UUID
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
planLevel: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Accounts');
}
};
migrations do user:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
type: Sequelize.UUID,
allowNull: false,
primaryKey: true,
default: Sequelize.UUID,
},
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
account_id: {
type: Sequelize.UUID,
allowNull: false,
default: Sequelize.UUID,
references: {
model: "Accounts",
key: "id",
},
onUpdate: "CASCADE",
onDelete: "CASCADE",
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
Após gera e fazer todas as alterações necessárias em migrations e model agora precisamos executar esse comando para criar as tabelas no bando:
npx sequelize-cli db:migrate
O projeto já está mais da metade resolvida, o precisamos agora é desenvolver as rotas, regra de negócio e o relacionamento entre as tabelas. Primeiro, vamos fazer o relacionamento entre as tabela, nisso precisamos ir à pasta do models/user e models/account para realizar algumas alterações. Lembrando que precisamos criptografar a senha do usuário antes de salvarmos esse dado no nosso banco de dados, então vamos primeiro para models/user:
//em associate adiciona esse código para fazer o relacionamento entre as tabelas
static associate(models) {
this.belongsTo(models.Account, { foreignKey: "account_id", as: "accounts" });
}
//após o modelName: "User", adicione esse código para que antes dele criar o usuário ele criptografar o valor da senha
hooks: {
beforeCreate: (user, options) => {
return bcrypt
.hash(user.password, 10)
.then((hash) => {
user.password = hash;
})
.catch((err) => {
throw new Error(err);
});
},
},
No models/account, vamos alterar o associate para:
static associate(models) {
this.hasOne(models.User, { foreignKey: "account_id", as:"accounts"});
}
Esse é um relacionamento de um pra um, então estou usando hasOne caso fosse de um pra muitos devemos utilizar hasMany.
Caso precise mudar o nome da tabela em migrations
você gera outra migrations com um nome que indique o que essa migrations representa. O primeiro argumento é um nome da tabela que está no banco de dados, e o segundo argumento é o nome que a tabela receberá.
example:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.renameTable('accounts', 'Accounts');
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Accounts');
}
};
Router e Controller
Agora vamos finalizar o projeto criado as rotas da api, mas primeiro vamos criar dos arquivos na pasta controller um chamado de controller/Account.js e controller/User.js. Nesses arquivos estarão a regra de negócio do projeto e a comunicação com o banco de dados. Nome do arquivo com letra maiúscula, pois, vamos criar uma classe.
controller/Account.js
const model = require("../models");
class Account {
async store(DAO) {
try {
const account = await model.sequelize.models.Account.create({
name: DAO.name,
planLevel: DAO.planLevel,
});
return account;
} catch (error) {
throw new Error(error);
}
}
async show() {
try {
const accounts = await model.sequelize.models.Account.findAll();
return accounts;
} catch (error) {
throw new Error(error);
}
}
}
module.exports = Account;
controller/User.js
const model = require("../models");
class User {
async store(DAO) {
try {
const user = await model.sequelize.models.User.create({
firstName: DAO.firstName,
lastName: DAO.lastName,
email: DAO.email,
password: DAO.password,
account_id: DAO.account_id,
});
return user;
} catch (error) {
throw new Error(error);
}
}
//no attributes informo os campos que eu quero que retorna do select
///include estou incluido a associação feita com o account, então ele vai retornar o account daquele user
async show() {
try {
const users = await model.sequelize.models.User.findAll({
attributes: ['id', 'firstName', 'lastName', 'email', 'account_id'],
include: {
attributes: ['id', 'name'],
association: "accounts",
},
});
return users;
} catch (error) {
throw new Error(error);
}
}
}
module.exports = User;
Para finalizar o Projeto, vamos fazer as rotas primeiro precisamos criar dos arquivos na pasta router um chamado de router/user.js e router/account.js no arquivo router/account.js fica o seguinte código:
const { Router } = require("express");
const AccountController = require("../controller/Account");
const accountController = new AccountController();
const router = Router();
router.get("/", async (req, res) => {
try {
const result = await accountController.show();
res.status(200).send(result);
} catch (error) {
res.status(400).send(error);
}
});
router.post("/", async (req, res) => {
try {
const result = await accountController.store(req.body);
res.status(200).send(result);
} catch (error) {
res.status(400).send(error);
}
});
module.exports = router;
no router/user.js
const { Router } = require("express");
const UserController = require("../controller/User");
const userController = new UserController();
const router = Router();
router.get("/", async (req, res) => {
try {
const result = await userController.show();
res.status(200).send(result);
} catch (error) {
res.status(400).send(error);
}
});
router.post("/", async (req, res) => {
try {
const result = await userController.store(req.body);
res.status(200).send(result);
} catch (error) {
res.status(400).send(error);
}
});
module.exports = router;
No arquivo router/index.js temos que adicionar as rotas criadas, então fica assim o arquivo router/index.js.
const { Router } = require("express");
const user = require('./user');
const account = require('./account');
const router = Router();
router.use('/user', user);
router.use('/account', account);
router.get('/', async (req, res) => {
res.send('api running!');
});
module.exports = router;
Portanto, o projeto está pronto!
Referências:
Orlando, Claudio. Configurando o ORM Sequelize no NodeJS com ExpressJS. Rocketseat, 2017. Disponivel em: https://blog.rocketseat.com.br/nodejs-express-sequelize/. Acesso em: 18, Fev. de 2021
Node.js - O que é, como funciona e quais as vantagens.A Opus Software, 2018. Disponivel em: https://www.opus-software.com.br/node-js/. Acesso em: 22 de Fev. de 2021.
Top comments (4)
Boa paulin
obrigado!
Show, me ajudou muito!
Que bom