Web Dev Drops

Fullstack com Node.js, React e GraphQL — Parte 3: PostgreSQL e Sequelize

📅 15/03/2018 • 🕙7 min. de leitura

Olá, pessoal! Neste terceiro post da série Fullstack com Node.js, React e GraphQL vamos adicionar o banco de dados PostgreSQL, fazendo o mapeamento entre o banco e a aplicação com Sequelize.

1 1WK3mKHG11Dwq e3V nK g

Esquema do banco

Nosso banco de dados vai começar simples:

1 LTjQGVt7pF mt1RiPDpNrA

Esquema do banco

Temos a representação da Corretora (Broker). Cada corretora possui N Investimentos (Investments), e cada investimento pode possuir N Transações (Transactions), que são depósitos e retiradas feitas naquele investimento, e N Atualizações de Saldo (Balance Updates), que são mudanças no saldo do investimento decorrentes do rendimento daquela aplicação.

PostgreSQL

Vamos guardar estes dados em um banco PostgreSQL, pelos motivos:

  • É gratuito e bem difundido, com bastante documentação disponível.
  • É um dos bancos suportados pelo Sequelize, que era outra ferramenta que queria usar para aprendizado.
  • O Heroku, serviço onde vamos colocar a aplicação em produção, oferece uma instância de PostgreSQL gratuita.

Instalação local

Em produção vamos usar o banco fornecido pelo Heroku, mas para desenvolvimento vamos precisar ter um banco local.

No site oficial do PostgreSQL tem opções de download para seu sistema operacional e instruções de instalação. No MacOS eu uso e recomendo o Postgres.app, uma interface simples para instalar e executar o Postgres.

Se você curte Docker, uma opção é usar um container com a imagem do Postgres. Não vou entrar em detalhes aqui para o post não ficar longo.

Criação do usuário no banco

Antes de começar a criar a tabelas, precisamos criar um usuário específico para a nossa aplicação dentro do Postgres.

Evite usar o usuário padrão, chamado postgres, em suas aplicações, por motivo de segurança, pois ele tem todas as permissões no banco inteiro, podendo alterar e apagar tudo.

Vamos usar o comando psql para acessar o banco.

Uma vez conectado, você vai estar em uma outra linha de comando, onde pode usar comandos específicos no Postgres. um deles é o \du, que lista os usuários existentes no banco:

1 p64ACI0ZRn dfQmVdojA A

Vamos criar o usuário mymoney para a aplicação (pode escolher uma senha mais segura 😉):

CREATE ROLE mymoney WITH LOGIN PASSWORD '123456';

E dar permissão para ele criar bancos de dados:

ALTER ROLE mymoney CREATEDB;

Se executar um \du novamente, vai ver o novo usuário criado. Você pode usar \q para sair do psql.

Sequelize

Sequelize é um ORM (Object-Relational Mapper) para Node.js. Eles faz o mapeamento de dados relacionais (armazenados tabelas, linhas e colunas) para objetos em JS. Ele permite criar, buscar, alterar e remover dados do banco usando objetos e métodos em JS, além de fazer alterações na estrutura das tabelas. Ele suporta os bancos PostgreSQL, MySQL, MSSQL e SQLite.

Instalação

Vamos instalar o Sequelize e o módulo pg, para trabalhar com Postgres:

npm i sequelize pg

Vamos instalar também a ferramenta de linha de comando do Sequelize (CLI) como dependência de desenvolvimento:

npm i -D sequelize-cli

Inicialização

Vamos usar o sequelize-cli para gerar alguns arquivos iniciais, mas antes vamos configurar onde ele vai criar esses arquivos. Vamos criar um arquivo .sequelizerc na raiz, com o conteúdo:

const path = require('path');

module.exports = {  
  config: path.resolve('config/database.js'),  
  'migrations-path': path.resolve('db/migrate'),  
  'seeders-path': path.resolve('db/seeds'),  
  'models-path': path.resolve('src/models')  
};

Já já explico o que são esses arquivos. Agora vamos rodar no terminal:

npx sequelize init

Este comando vai criar os arquivos iniciais do Sequelize.

npx é um comando que já vem com o NPM a partir da versão 5.2.0. Ele roda um executável que foi instalado via NPM, sem precisar passar o caminho do executável nem instalá-lo globalmente. No exemplo acima ele vai executar o sequelize que está nas nossas dependências (pasta node_modules).
Mais detalhes: https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b

Configuração

O arquivo config/database.js contém as informações de conexão ao banco, separadas em 3 ambientes: desenvolvimento, testes e produção. Vamos configurar apenas o ambiente de desenvolvimento por enquanto:

development: {  
  username: 'mymoney',  
  password: '123456',  
  database: 'mymoney',  
  host: '127.0.0.1',  
  dialect: 'postgres'  
},

E essa senha escancarada aí? Não podemos comitar isso.

Vamos criar um arquivo de senhas e outras configurações sigilosas, config/secret.js:

module.exports = {  
  DATABASE_PASSWORD: '123456'  
};

Adicionar config/secret.js no .gitignore e usá-lo nas configurações do banco:

const secret = require('./secret');
module.exports = {  
  development: {  
    username: 'mymoney',  
    password: secret.DATABASE_PASSWORD,      database: 'mymoney',  
    host: '127.0.0.1',  
    dialect: 'postgres'  
  },  
  test: ...,  
  production: ...  
};

Se o ESLint reclamar que você está usando um arquivo ignorado, adicione no seu .eslintrc:

rules: {  
  'node/no-unpublished-require': 'off'
}

Criação do banco

Para criar o banco, executamos o comando:

npx sequelize db:create

Modelos

Vamos criar nosso primeiro modelo: Broker (Corretora). Basta executar:

npx sequelize model:generate --name Broker --attributes name:string

Este comando vai criar dois arquivos: src/models/broker.js e db/migrate/20180311115229-create-broker.js

O primeiro é o modelo em si, que detalha os atributos do objeto Broker, seus tipos, regras de validação e relacionamento com outros modelos.

O segundo é o arquivo de migração, que descreve como esta alteração deve ser feita no banco de dados (no caso, será feita a criação de uma nova tabela).

Migracão (ou migration) é o processo alterar a estrutura de um banco de dados (criar/remover tabelas, colunas, alterar tipos e relacionamentos) de forma incremental e reversiva, e minimizando o impacto nos dados existentes.

Vamos fazer algumas alterações manuais nestes arquivos. No modelo, vamos adiciona uma validação no campo name para que ele seja obrigatório:

name: { type: DataTypes.STRING, allowNull: false }

Vamos fazer a mesma coisa no arquivo de migração, mas desta vez para adicionar a validação a nível do banco de dados:

name: {  
  allowNull: false,  
  type: Sequelize.STRING  
},

Segundo modelo e relacionamento

O próximo modelo, Investment (Investimento), tem uma peculiaridade. Ele tem uma relação com o Broker (Corretora), já que uma Corretora possui muitos Investimentos e um Investimento pertence a uma Corretora.

Primeiro, a geração dos arquivos de modelo e migração:

npx sequelize model:generate --name Investment --attributes name:string

Assim como no Broker, vamos adicionar a validação de obrigatoriedade no campo name, e também vamos especificar os relacionamentos.

No modelo de Investimento, dizemos que ele pertence a (belongs to) Corretora:

Investment.associate = function(models) {  
  this.belongsTo(models.Broker);};

E no arquivo de migração de Investimento adicionamos o campo BrokerId, que faz a ligação com a tabela de Corretora (chave estrangeira):

BrokerId: {  
   allowNull: false,  
   type: Sequelize.INTEGER,  
   onDelete: 'CASCADE',  
   references: {  
     model: 'Brokers',  
     key: 'id'  
   }  
 },

Por fim, no modelo de Corretora dizemos que ele possui muitos (has many) Investimentos:

Broker.associate = function(models) {  
  this.hasMany(models.Investment);};

Outros modelos

Para finalizar, a criação dos outros modelos e migrações:

npx sequelize model:generate --name Transaction --attributes amount:decimal,date:dateonly

npx sequelize model:generate --name BalanceUpdate --attributes amount:decimal,date:dateonly

Lembrando de adicionar as relações: Investimento possui muitas Transações e Investimento possui muitas Atualizações de Saldo.

Nos modelos e migrações, além das validações de obrigatoriedade, também adicionei precisão nos campos de valores:

amount: { type: **DataTypes.DECIMAL(16, 2)**, allowNull: false }

Você pode ver aqui o resultado final dos modelos: 
https://github.com/doug2k1/my-money/tree/v2.0.0/src/models
e migrações:
https://github.com/doug2k1/my-money/tree/v2.0.0/db/migrate

Executando as migrações

Ok, os modelos estão descritos no nosso código, mas ainda precisamos fazer as alterações no banco:

npx sequelize db:migrate

Para conferir que as tabelas foram criadas, podemos acessar o psql com o usuário mymoney, listar as tabelas com \dt e ver as informações de cada tabela com \d+ "NomeDaTabela":

psql -U mymoney

mymoney=> \dt  
  
mymoney=> \d+ "Brokers"

Testando tudo

Para testar nosso banco e modelos, podemos adicionar o seguinte código no nosso server (src/index.js):

const { Broker, Investment, Transaction, BalanceUpdate } = require('./models');

const test = async () => {
  // create broker
  const broker = await Broker.create({ name: 'Fooinvest' });
  // create investment
  const investment = await Investment.create({
    name: 'Tesouro Foo',
    BrokerId: broker.get('id')
  });
  // create transaction
  await Transaction.create({
    amount: 500,
    date: '2018-03-10',
    InvestmentId: investment.get('id')
  });
  // create balance update
  await BalanceUpdate.create({
    amount: 501,
    date: '2018-03-12',
    InvestmentId: investment.get('id')
  });
  // select all
  const brokerWithDetails = await Broker.findOne({
    include: [
      {
        model: Investment,
        include: [{ model: Transaction }, { model: BalanceUpdate }]
      }
    ]
  });
  console.log(JSON.stringify(brokerWithDetails));
};

test();

Ele importa os modelos e usa as funções do Sequelize para criar entidades (ex: Broker.create) e buscar no banco (ex: Broker.findOne).

Mais detalhes da API de model do Sequelize e todas as funções disponíveis para criação de busca de dados: http://docs.sequelizejs.com/manual/tutorial/models-usage.html

Vamos explorar mais estes modelos nas próximas partes.

Resultado final

O código do projeto até este ponto está em: https://github.com/doug2k1/my-money/tree/v2.0.0

No próximo capítulo

Na próxima parte vamos criar a interface administrativa usando Forest Admin. Stay tuned!

Feedbacks?

E aí, o que está achando até agora? Algo que precisa melhorar?

[]’s

🔥 Pacote Fullstack | 4 Cursos Completos

A Danki Code está oferecendo uma formação para desenvolvedor fullstack bastente completa, englobando frontend, backend, UX/UI, SEO, etc.
Saiba mais…


Douglas Matoso

Por Douglas Matoso, desenvolvedor frontend.


Comentários