Iniciando projeto Nodejs com ORM Sequelize

fs_pauloo

Paulo

Posted on March 15, 2021

Iniciando projeto Nodejs com ORM Sequelize

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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'));
Enter fullscreen mode Exit fullscreen mode

No terminal execute esse comando:

node app.js
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Altere para:

//node_modules
//src
////config
////controller
////db
//////migrations
//////seeders
////models
////router
////index.js
//app.js
//package.json
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

O arquivo config.js altere seu conteúdo para:

module.exports = {
  username: "",
  password: "",
  database: "",
  host: "",
  port: ,
  dialect: "postgres",
  define: {
    timestamps:true
  }
};
Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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')
};

Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
  });

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
  }
};
Enter fullscreen mode Exit fullscreen mode

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');
  }
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
        });
    },
},
Enter fullscreen mode Exit fullscreen mode

No models/account, vamos alterar o associate para:

static associate(models) {
    this.hasOne(models.User, { foreignKey: "account_id", as:"accounts"});
}
Enter fullscreen mode Exit fullscreen mode

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');     
  }
};
Enter fullscreen mode Exit fullscreen mode

Saiba Mais

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;
Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
fs_pauloo
Paulo

Posted on March 15, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related