Sequelize com NodeJS

Sequelize com NodeJS

Olá leitor, seja muito bem vindo a mais uma lição que envolve o manuzeio de banco de dados com NodeJS.

Dessa vez, iremos aprender a utilizar a biblioteca do Sequelize, para realizar comunicações com a nossa base de dados que foi feita com MySQL.

Mas antes de entedermos o funcionamento dessa biblioteca, eu gostaria que você desse uma olhada no meu artigo que fala sobre: O que são ORMs? E quais as vantagens de os utilizar em seus projetos?

Entendeu o que são ORMs, e quais seus principais benefícios em aplicações feitas com NodeJS?

Então vamos aprender sobre esse tal de Sequelize 😄

O que é o Sequelize?

O Sequelize nada mais é do que um ORM (Object-Relational Mapper) feito para o ambiente do NodeJS.

Seu objetivo, é facilitar a interação com bancos de dados relacionais, como é o caso dos bancos: MySQL, PostgreSQL, SQLite, e MariaDB.

Ele permite que os desenvolvedores definam modelos de dados e façam consultas, inserções, atualizações e exclusões no banco de dados usando JavaScript, em vez de escrever SQL manualmente.

É importante ressaltar que o Sequelize segue o padrão Active Record, isso significa que cada classe de modelo é responsável por gerenciar tanto os dados, quanto a lógica associada a eles.

Com relação as principais funcionalidades do Sequelize, podemos destacar:

Mapeamento de Modelos: com ele, você pode definir modelos em JavaScript que mapeiam nossas tabelas do banco de dados.

Migrações: você terá acesso a ferramentas para gerenciar, e aplicar alterações no esquema do banco de dados (como criar ou modificar tabelas).

Consultas: possuí suporte a consultas SQL complexas usando uma sintaxe de JavaScript simplificada.

Validação e Manipulação de Dados: ele oferece validações embutidas e permite definir comportamentos personalizados para os seus dados.

Relacionamentos: ele também facilita o mapeamento de relacionamentos entre tabelas (como um-para-um, um-para-muitos e muitos-para-muitos).

Criando nosso projeto de testes

Antes de colocarmos a mão na massa, é deveras importante que você configure o seu projeto inicial.

Para isso, eu criei uma pasta chamada de Sequelize dentro da minha área de trabalho (desktop):

Com o seu terminal (Prompt de Comando) aberto na pasta raiz que acabamos de criar, precisamos inicializar o nosso projeto por meio do NPM, sendo assim, execute o seguinte comando abaixo:

npm init -y

A flag -y, como você já deve saber, cria um novo projeto de forma enxuta, respondendo SIM para tudo 😅

Por fim, não se esqueça de criar seu index.js, com uma mensagem bem legal de boas vindas:

console.log('Olá Mundo!');

Criando a sua tabela no banco de dados

Na lição passada, eu te ensinei a configurar algumas coisas, como:

Como está lição se trata de uma jornada, gostaria que você voltasse na lição anterior, e realizasse todos os passos necessários para configurar o MySQL junto com a base (nodejs), sem se esquecer de criar também uma tabela chamada de usuarios.

Feito isso, você já esta pronto para o Sequelize 😌

Instalando o Sequelize

Para instalar a biblioteca do Sequelize no seu projeto, é bem simples, com o seu terminal (Prompt de Comando) aberto na pasta raiz do seu projeto, execute o seguinte comando abaixo:

npm install sequelize

Além disso, como estaremos conectando o Sequelize no nosso banco de dados MySQL, ele vai acabar dependendo de um outro pacote chamado de mysql2 (que você já deve saber como funciona).

Portanto, com o seu terminal (Prompt de Comando) ainda aberto, execute mais uma operação de instalação:

npm install mysql2

É importante ressaltar que o Sequelize é baseado em Promises do Javascript, ou seja, estruturas assíncronas que executam métodos como then() e catch().

Configurando o Sequelize

Após a instalação da biblioteca, o processo de configuração ocorre de forma bem simples.

Primeiro, por questões de organização de código, o ideal é que você crie uma nova pasta chamada de db (dentro da pasta raiz do seu projeto), onde dentro dela exista um arquivo chamado de connection.js:

Dessa forma, nós iremos conseguir separar em um módulo específico, o arquivo responsável por realizar a conexão com a nossa base de dados.

Já dentro do connection.js, vamos inserir a seguinte lógica:

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('nodejs', 'root', 'senha-do-root', {
    host: 'localhost',//Pode ser 127.0.0.1 ou qualquer outro IP Externo
    dialect: 'mysql'//Pode ser mysql, postgres, sqlite, mariadb ou mssql
});

try{
    sequelize.authenticate();//Testar a conexão com o banco de dados
    console.log('Conexão estabelecida com sucesso.');
}catch(error){
    console.error('Não foi possível conectar ao banco de dados:', error);
}

module.exports = sequelize;

const { Sequelize }: aqui nós estamos importando a classe do Sequelize da sua própria biblioteca (require('sequelize')).

new Sequelize: aqui nós estamos instanciando a classe do Sequelize, e salvando-a dentro da constante com o mesmo nome (sequelize). A classe espera receber 4 argumentos:

1° Argumento (nodejs): é o banco de dados que a biblioteca irá se conectar.

2°  Argumento (root): é o nome do usuário que executará essa conexão com o banco.

3° Argumento ('senha-do-root'): é a senha relacionada ao usuário que foi passado via 2° argumento.

4° Argumento: é um objeto que conta com duas chaves. host que é a URL de conexão com o banco, e dialect que é o tipo de banco de dados que estamos nos conectando.

No caso da chave dialect, como estamos usando o banco MySQL, é bem obvio que ele precisa ser declaro alí (mysql).

Caso estivéssemos usando um outro banco de dados (como SQLite, por exemplo) precisaríamos definí-lo alí dentro, e também instalar uma outra biblioteca para se trabalhar com ele (sem ser a mysql2, pois ela não seria mais necessária, visto que o banco de dados não é mais o MySQL).

Em seguida, fizemos o uso da estrutra Try...Catch para executar o método authenticate() do Sequelize, que é responsável por se comunicar com a nossa base de dados.

Por fim, exportamos a variável que contém uma instancia da classe do Sequelize, por meio do module.exports, fizemos isso para que este arquivo (connection.js) seja transformado em um módulo.

Por fim, não se esqueça de importar esse módulo para o seu index.js, e rodar a sua aplicação (node ./index.js):

const connection = require('./db/connection');
console.log('Servidor Iniciado com Sucesso!');

Se tudo estiver OK, você receberá a seguinte mensagem no console:

Indicando que a comunicação com a sua base de dados por meio do Sequelize, foi um sucesso 😁

O que significa o comando SELECT 1+1 AS result que vemos no console?

Quando o NodeJS executa o Sequelize, e você vê o comando SELECT 1+1 AS result nos logs, isso quer dizer que o Sequelize fez uma verificação de "saúde", ou testou a conexão com o banco de dados ao inicializar.

O Sequelize então, envia uma consulta simples como SELECT 1+1 AS result para garantir que o banco de dados esteja acessível pela nossa aplicação, e a conexão esteja funcionando corretamente.

Essa consulta, geralnente é leve e não envolve acesso direto às nossas tabelas, sendo ideal para verificar a conectividade.

Sendo assim, se você esta vendo esse comando no console, significa que o Sequelize está validando se a comunicação com o banco de dados foi bem-sucedida, antes de continuar executando as operações reais.

Criando o nosso Model

Como premissa de qualquer ORM do mercado, a maioria deles, pedem para que o desenvolvedor crie uma pasta chamada de model (ou models) para agrupar a representação do nosso banco de dados em arquivos .js que representam classes.

Esses models, nada mais são do que arquivos que representam as tabelas do nosso banco de dados, só que em um formato de classes do Javascript.

O que vai ao encontro dos conceitos ensinados no artigo que fala sobre ORM, uma vez que aqui, nós não vamos precisar criar querys SQL de forma direta 😉

É por meio do model, que o Sequelize vai fazer a comunicação com o nosso banco de dados (MySQL), sendo assim, é por meio dele que conseguiremos ler dados, atualizar dados, inserir dados e também deletar dados.

Para usar um Model, você precisa instanciar uma classe que representará uma tabela, mas pra isso, você vai precisar criar esse arquivo primeiro 😉

Dentro da pasta raiz do seu projeto, vamos criar uma nova pasta chamada de models:

Dentro dessa pasta, vamos criar o nosso primeiro model, que vai ser chamado de Usuarios.js:

Esse arquivo será representado por uma classe, que é responsável por trabalhar exclusivamente com a tabela usuarios do nosso banco de dados.

Observação: caso você tenha mais de uma tabela, a ideia, é que você possa representar cada tabela, por meio de diferentes arquivos dentro da pasta models.

Já dentro do arquivo Usuarios.js, vamos inserir a seguinte lógica:

const { DataTypes } = require('sequelize');

const connection = require('../db/connection');

const Usuarios = connection.define('usuarios', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        require: true
    },
    senha: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

Antes de explicarmos tudo o que está acontecendo no código acima, gostaria que você desse uma olhada na estrutura da tabela usuários (que você configurou desde a lição passada):

CREATE TABLE usuarios (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nome VARCHAR(100) NOT NULL,
  email VARCHAR(100) NOT NULL,
  senha VARCHAR(255) NOT NULL,
  data_criacao datetime NOT NULL
);

Feito isso, vamos às explicações 😉

{ DataTypes }: é um objeto importado do Sequelize que contém os tipos de dados (como STRING, INTEGER, etc.), que você vai usar para definir os campos da tabela.

Observação: a ideia do Sequelize, é que você "recrie" cada uma de suas tabelas e colunas do seu banco de dados, dentro de classes do Javascript.

connection: A variável connection importa a configuração da conexão com o banco de dados, definida no arquivo ../db/connection. Precisamos desse módulo para interagir com o nosso banco de dados.

connection.define('usuarios', {...}): Esse método cria um modelo do Sequelize chamado Usuarios que está mapeado para uma tabela chamada usuarios no banco de dados. A tabela será criada ou usada com base na conexão já definida.

É interessante ressaltar que se a tabela usuarios não existir na nossa base de dados, quando chamarmos esse model, ele passará a existir, pois o Sequelize a criará de forma automática (isto é, caso estivermos usando o método sync para isso).

nome: representa a nossa coluna nome da tabela usuários, definimos que ela é do tipo STRING (texto), e que não pode ser nula (allowNull: false).

email: representa a nossa coluna email da tabela usuários, definimos que ela é do tipo STRING, mas que também não pode ser nula. No caso dessa coluna, ela é obrigatória (required: true).

senha: representa a nossa coluna senha da tabela usuários, definimos que ela é do tipo STRING, igualmente não pode ser nula.

DataTypes.STRING: define que os campos nome, email e senha são do tipo string (texto).

allowNull: false: impede que esses campos tenham valores nulos, ou seja, eles se tornam obrigatórios na hora de inserir um novo registro na tabela.

module.exports = Usuarios: estamos exportando esse arquivo como um módulo, pois ele será utilizado futuramente por outros arquivos que irão trabalhar com banco de dados (mais especificamente com a tabela de usuarios).

Resumidamente, a lógica do arquivo acima (Usuarios.js) está executando os seguintes procedimentos:

  • Está configurando a conexão com o banco de dados.
  • Define um modelo chamado Usuarios para mapear uma tabela usuarios.
  • Configura três campos obrigatórios na tabela: nome, email e senha, onde todos são do tipo string.

Feito isso, já temos toda a configuração necessária para realizarmos nossas operações do tipo CRUD (Create, Read, Update e Delete) no banco de dados 🥳

Atualizando o arquivo connection.js e index.js

Anteriormente nesta lição, nós usamos o método autenticated() do Sequelize para testarmos a nossa a conexão com a tabela do banco de dados.

Da forma como o arquivo connection.js está sendo chamado dentro do index.js:

const connection = require('./db/connection');

console.log('Servidor Iniciado com Sucesso!');

Pode ocasionar um grande problema de geração de tabelas do nosso banco de dados.

Pois o ideal, é que o Sequelize verifique se todas as nossas tabelas (models) realmente existem no banco de dados (MySQL) , antes de abrir uma conexão com a nossa base de dados.

Para resolver isso, vamos precisar remover (ou comentar/desativar) a execução do bloco Try...Catch do arquivo connection.js:

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('nodejs', 'root', 'suaSenha', {
    host: 'localhost',//Pode ser 127.0.0.1 ou qualquer outro IP Externo
    dialect: 'mysql'//Pode ser mysql, postgres, sqlite, mariadb ou mssql
});

/*try{
    sequelize.authenticate();//Testar a conexão com o banco de dados
    console.log('Conexão estabelecida com sucesso.');
}catch(error){
    console.error('Não foi possível conectar ao banco de dados:', error);
}*///Não vamos precisa disso, pois a comunicação será feita pelo método SYNC do Sequelize

module.exports = sequelize;

Além disso, dentro do index.js, você vai precisar usar o método sync() da seguinte forma:

const connection = require('./db/connection');

connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

console.log('Servidor Iniciado com Sucesso!');

Basicamente, nós não estamos deixando a nossa aplicação funcionar (pela primeira vez), sem antes o Sequelize garantir que todas as tabelas necessárias para o projeto estão criadas.

Observação: se você estiver usando o express para o gerenciamento de rotas dentro da sua aplicação, é recomendável que você só execute o método app.listen após a execução do then() do método sync(), observe como isso pode ser feito:

connection.sync().then(() => {
    app.listen(3000);//Só roda a aplicação quando o Sequelizer garantir que todas as tabelas existem no banco
});

Lembrando que o método sync() cria nossas tabelas caso elas não existam, ok?

Mas... para que o sync() reconheça as tabelas que precisam ser criadas, é necessário que você importe todos os seus models para dentro do mesmo arquivo onde o método sync() está sendo chamado:

const connection = require('./db/connection');
const usuarios = require('./models/Usuarios');

connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

console.log('Servidor Iniciado com Sucesso!');

Isso ocorre porque, o Sequelize precisa conhecer os modelos (e, consequentemente, as tabelas) no momento em que o sync() é executado.

Se a sua tabela já existir, você receberá a seguinte mensagem no console:

Caso a sua tabela não exista, o Sequelize, por de baixo dos panos, vai se encarregar de criar ela para você:

Lembrando que o id é criado automaticamente pelo Sequelizer., sendo assim, você não precisa definí-lo dentro do seu model.

Eu quero um id diferente, e agora?

Em algumas aplicações, alguns administradores de banco de dados, costumam nomear suas chaves primarias (famoso id) com nomes diferenciados, por exemplo:

  • id_usuario
  • id_user
  • userID
  • e etc...

Caso você queira seguir uma convenção diferenciada, o Sequelize permite a customização da chave primária por meio da propriedade primaryKey: true.

Lembrando que isso deve ser feito dentro do seu model. Vamos ver um exemplo:

const { DataTypes } = require('sequelize');
const connection = require('../db/connection');

const Usuarios = connection.define('usuarios', {
    id_usuario: {
        type: DataTypes.INTEGER,  // Tipo inteiro
        autoIncrement: true,      // Incremento automático
        primaryKey: true,         // Define como chave primária
    },
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        require: true
    },
    senha: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

module.exports = Usuarios;

No caso do comando acima, estamos dizendo que a tabela usuarios possuí uma coluna chamada id_usuario que representa um identificador único relacionado a tabela.

Removendo os campos createdAt e updatedAt

Nem sempre, nossas tabelas farão o uso dos campos created_at e updated_at, que por sinal, já estão sendo configurados de maneira padrão, sempre quando o Sequelize precisa criar uma tabela (caso ela não exista).

Experimente excluir a sua tabela usuarios do seu banco de dados, e depois rodar a sua aplicação do NodeJS:

WOW... ao que tudo indica, o Sequelize criou de forma automática as colunas createdAt e updatedAt, sem a minha permissão 😮

Isso aconteceu, porque o Sequelize, por padrão, adiciona automaticamente as colunas createdAt e updatedAt para rastrear quando os registros são criados e atualizados.

Essas colunas fazem parte do comportamento padrão do Sequelize para fornecer um timestamp (registro de data e hora), em cada operação de criação e atualização de dados.

Para remover essas colunas, você pode desativá-las usando a opção timestamps: false, da seguinte forma:

const { DataTypes } = require('sequelize');
const connection = require('../db/connection');

const Usuarios = connection.define('usuarios', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        require: true
    },
    senha: {
        type: DataTypes.STRING,
        allowNull: false
    }
}, {
    timestamps: false  // Desativa as colunas createdAt e updatedAt
});

module.exports = Usuarios;

Caso desejar, você também pode renomear o nome dessas colunas para outra qualquer:

const Usuarios = connection.define('usuarios', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        require: true
    },
    senha: {
        type: DataTypes.STRING,
        allowNull: false
    }
}, {
    createdAt: 'data_criacao',   // Personaliza o nome da coluna createdAt
    updatedAt: 'data_atualizacao' // Personaliza o nome da coluna updatedAt
});

Adicionando um campo do tipo data

Para criar um campo que representa uma data (DATETIME), você pode fazer isso da seguinte forma:

const Usuarios = connection.define('usuarios', {
    id_usuario: {
        type: DataTypes.INTEGER,
        autoIncrement: true,
        primaryKey: true,
    },
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        require: true
    },
    senha: {
        type: DataTypes.STRING,
        allowNull: false
    },
    dataCriacao: {
        type: DataTypes.DATE,  // Define como DATETIME
        allowNull: false,
        defaultValue: DataTypes.NOW // Define o valor padrão como a data/hora atual
    }
}, {
    timestamps: false  // Desativa createdAt e updatedAt automáticos
});

Observe que criamos uma coluna chamada dataCriacao, que será do tipo DATETIME (DataTypes.DATE), onde o valor padrão dela, será a data de hoje (defaultValue: DataTypes.NOW)

Note que o allowNull está setado para false, isso significa que o usuário não precisa informar a dataCriacao quando estiver fazendo um INSERT na tabela.

Entretanto, o Sequelize vai se responsabilizar por inserir a data atual (DataTypes.NOW) sempre quando você não informar um valor para a dataCriacao.

Conhecendo mais sobre DataTypes

O Sequelize oferece uma variedade de tipos de dados através do objeto DataTypes.

O DataTypes funciona de forma similar ao tipo de dado que uma coluna pode receber. No MySQL por exemplo, nós temos diversos tipos de dados como:

  • VARCHAR
  • BOOLEAN
  • INT
  • e etc...

Cada um desses tipos de dados podem ser representados em DataTypes, e é dessa forma que o Sequelize sabe o tipo de dado de cada coluna que ele irá trabalhar 😉

Abaixo veremos uma grande lista onde conta com a maioria dos DataTypes que podemos usar: 

DataTypes.STRING: representa uma string de texto.

DataTypes.STRING(length): string com comprimento máximo especificado (em caracteres).

DataTypes.TEXT: representa um texto de tamanho variável. Pode ser usado para textos longos.

DataTypes.INTEGER: representa um número inteiro.

DataTypes.BIGINT: representa um número inteiro grande.

DataTypes.FLOAT: representa um número de ponto flutuante.

DataTypes.DOUBLE: representa um número de ponto flutuante de precisão dupla.

DataTypes.DECIMAL(precision, scale): representa um número decimal com precisão e escala especificadas.

DataTypes.BOOLEAN: representa um valor booleano (true ou false).

DataTypes.DATE: representa uma data e hora.

DataTypes.DATEONLY: representa uma data sem a parte de hora.

DataTypes.TIME: representa uma hora sem a data.

DataTypes.JSON: representa um valor JSON.

DataTypes.JSONB: representa um valor JSON binário (mais eficiente para alguns bancos de dados, como PostgreSQL).

DataTypes.UUID: representa um UUID (Identificador Universal Único).

DataTypes.BLOB: representa dados binários grandes.

DataTypes.ENUM(values): representa um conjunto de valores possíveis. Por exemplo, DataTypes.ENUM('active', 'inactive').

DataTypes.RANGE: representa um intervalo de valores. Este tipo é suportado apenas em alguns bancos de dados, como PostgreSQL.

DataTypes.GEOMETRY: representa dados geométricos. Usado para tipos de dados espaciais em alguns bancos de dados, como é o caso do MySQL.

Vejamos alguns exemplos de uso dos DataTypes 🙃

STRING com comprimento máximo:

nome: {
    type: DataTypes.STRING(100), // Máximo de 100 caracteres
    allowNull: false
}

DECIMAL com precisão e escala:

preco: {
    type: DataTypes.DECIMAL(10, 2), // Até 10 dígitos no total, com 2 casas decimais
    allowNull: false
}

ENUM:

status: {
    type: DataTypes.ENUM('ativo', 'inativo'),
    allowNull: false
}

Para mais informações sobre os DataTypes, não deixe de consultar a documentação do Sequelize.

Inserindo dados na tabela

Enfim, chegou o momento de aprendermos a realizar uma das operações de CRUD na nossa tabela de usuários.

A primeira operação que veremos é a de INSERT (inserção de dados), e o procedimento para fazer isso é bem simples 🙂

Antes de mais nada, nós vamos precisar do model responsável por se conectar a nossa tabela de usuários (isso nós já temos ✅).

Já para realizar um INSERT na tabela usuarios usando Sequelize, você deve utilizar o método create para isso.

Esse método insere um novo registro na tabela, e retorna a instância que foi criada.

Sendo assim, certifique-se de que seus modelos estão inseridos dentro do index.js:

const { Sequelize, DataTypes } = require('sequelize');
const connection = require('./db/connection'); // Ajuste o caminho conforme necessário
const Usuarios = require('./models/Usuarios'); // Ajuste o caminho conforme necessário

Em seguida, basta chamar uma função assíncrona, que será responsável por fazer o INSERT na sua tabela do banco de dados:

async function inserirUsuario() {
    try {
        // Dados a serem inseridos
        const novoUsuario = await Usuarios.create({
            nome: 'João Silva',
            email: 'joao.silva@example.com',
            senha: 'senhaSegura123'
        });

        console.log('Usuário inserido com sucesso:', novoUsuario.toJSON());
    } catch (error) {
        console.error('Erro ao inserir usuário:', error);
    }
}

// Chame a função para realizar o INSERT
inserirUsuario();

Note que no comando acima, chamamos o método create() que existe (de forma implicita) dentro do modelo Usuarios.

Observação: o método create não precisa ser definido explicitamente no seu modelo Usuarios.js, uma vez que o Sequelize já fornece esse método como parte de sua API para todos os modelos que você definir.

Pois a partir do momento que você usa o comando connection.define(), por de baixo dos panos, o método create() também esta sendo criado, a única diferença é que você não o vê...

Note que dentro do método create, estamos informando valores fictícios para cada uma das chaves (que representam colunas da nossa tabela) existentes em nosso Model (Usuarios).

Além disso, a constante novoUsuario, é responsável por armazenar o retorno dessa operação, que no nosso caso, tal retorno está sendo mostrado no console por meio do comando .toJSON().

O método .toJSON() retorna em formato JSON os registros que acabamos de inserir na nossa base de dados.

Veja como ficou o arquivo index.js:

const { Sequelize, DataTypes } = require('sequelize');

const connection = require('./db/connection'); // Ajuste o caminho conforme necessário
const Usuarios = require('./models/Usuarios'); // Ajuste o caminho conforme necessário

//Cria as tabelas no banco de dados caso não existam...
connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

//Define uma função de inserção de usuário
async function inserirUsuario() {
    try {
        // Dados a serem inseridos
        const novoUsuario = await Usuarios.create({
            nome: 'João Silva',
            email: 'joao.silva@example.com',
            senha: 'senhaSegura123'
        });

        console.log('Usuário inserido com sucesso:', novoUsuario.toJSON());
    } catch (error) {
        console.error('Erro ao inserir usuário:', error);
    }
}

// Chame a função para realizar o INSERT
inserirUsuario();

Ao executar o seu projeto, você vai se deparar com a seguinte mensagem de sucesso:

Indicando que os dados foram inseridos corretamente na sua tabela 🥳

E se caso a minha tabela não tivesse as colunas updatedAt e createdAt?

Caso você tenha desativado a criação das colunas updatedAt e createAt na hora em que definiu seu Model, você não precisa se preocupar com seu insert (método create), pois o Sequelize é inteligente o suficiente para entender que tais colunas não precisam ser preenchidas.

E se eu quisesse adicionar valores diferentes para as colunas updatedAt e createdAt no momento do INSERT?

Primeiro, você vai precisar garantir que dentro do seu Model, essas colunas não estão desabilitadas, e em seguida, você precisa passar os valores personalizados durante a criação de um novo registro, por exemplo:

// models/Usuarios.js
const { Sequelize, DataTypes } = require('sequelize');
const connection = require('../db/connection'); // Ajuste o caminho conforme necessário

// Definir o modelo de Usuários com as colunas padrão createdAt e updatedAt
const Usuarios = connection.define('Usuarios', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: true
    },
    senha: {
        type: DataTypes.STRING,
        allowNull: false
    }
}, {
    timestamps: true // Habilita as colunas padrão createdAt e updatedAt
});

module.exports = Usuarios;
async function inserirUsuario() {
    try {
        // Dados a serem inseridos com valores personalizados para createdAt e updatedAt
        const novoUsuario = await Usuarios.create({
            nome: 'João Silva',
            email: 'joao.silva@example.com',
            senha: 'senhaSegura123',
            createdAt: new Date('2023-01-01T10:00:00'), // Valor customizado para createdAt
            updatedAt: new Date('2024-09-17T12:00:00') // Valor customizado para updatedAt
        });

        console.log('Usuário inserido com sucesso:', novoUsuario.toJSON());
    } catch (error) {
        console.error('Erro ao inserir usuário:', error);
    }
}

// Chame a função para realizar o INSERT
inserirUsuario();

Note que no comando acima, nós estamos inserindo de forma manual as datas de criação e atualização.

Retornando dados da tabela

Para retornar todos os dados de uma determinada tabela (sem filtro de WHERE), você deve criar uma função assíncrona, que executa o método findAll() do seu Model.

E isso pode ser feito da seguinte forma:

const { Sequelize, DataTypes } = require('sequelize');

const connection = require('./db/connection'); // Ajuste o caminho conforme necessário
const Usuarios = require('./models/Usuarios'); // Ajuste o caminho conforme necessário

//Cria as tabelas no banco de dados caso não existam...
connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

//Define uma função para buscar usuários (sem filtro de WHERE)
async function buscarUsuarios() {
    try {
        // Busca todos os usuários
        const usuarios = await Usuarios.findAll();

        console.log('Usuários encontrados:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários:', error);
    }
}

// Chame a função para realizar a busca
buscarUsuarios();

Note que criamos uma função assíncrona que fica esperando o Sequelize retornos todos os dados existentes na tabela usuarios.

O método findAll() funciona de forma similar a query: "SELECT * FROM usuarios;".

Executando esse código no seu terminal (Prompt de Comando), teremos a seguinte resposta do NodeJS:

O que indica que o select foi realizado com sucesso 😉

Agora, se você quiser realizar uma busca com o filtro WHERE (e de forma segura), nós ainda fazemos o uso do método findAll(), porém,  passando o argumento WHERE dentro dele:

const { Sequelize, DataTypes } = require('sequelize');

const connection = require('./db/connection'); // Ajuste o caminho conforme necessário
const Usuarios = require('./models/Usuarios'); // Ajuste o caminho conforme necessário

//Cria as tabelas no banco de dados caso não existam...
connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

//Define uma função para buscar usuários (com filtro de WHERE)
async function buscarUsuariosComFiltro() {
    try {
        // Busca usuários com filtro
        const usuarios = await Usuarios.findAll({
            where: {
                nome: 'João Silva'
            }
        });

        console.log('Usuários encontrados:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários:', error);
    }
}

// Chame a função para realizar a busca
buscarUsuariosComFiltro();

No comando acima, estamos retornando todos os registros onde (where) a coluna nome possuí o valor "João Silva".

O comando acima é similar a query: "SELECT * FROM usuarios WHERE nome = 'João Silva';".

Supondo que você queira fazer um WHERE levando em consideração diversas colunas, para isso, basta adicionar mais chaves alí dentro, por exemplo:

const usuarios = await Usuarios.findAll({
  where: {
    nome: 'João Silva',
    senha: 'senhaSegura123'
  }
});

O comando acima é similar a query"SELECT * FROM usuarios WHERE nome = 'João Silva' AND senha = 'senhaSegura123';".

Observação: dentro do findAll(), você pode passar um parâmetro chamado { raw: true }. Esse parâmetro faz com que o resultado da consulta seja retornado como um objeto JavaScript plano, sem os métodos do modelo Sequelize.

Vejamos um exemplo de como usar { raw: true } com findAll():

const results = await Model.findAll({
  where: { condition: value },
  raw: true
});

console.log(results);

Usando o método findOne para retornar apenas um resultado

É por meio do método findOne do Sequelize, que você consegue retornar somente o primeiro registro que ele encontrar.

// Define uma função para buscar um único usuário (sem filtro de WHERE)
async function buscarUsuario() {
    try {
        // Busca o primeiro usuário encontrado
        const usuario = await Usuarios.findOne();
        
        if (usuario) {
            console.log('Usuário encontrado:', usuario.toJSON());
        } else {
            console.log('Nenhum usuário encontrado.');
        }
    } catch (error) {
        console.error('Erro ao buscar o usuário:', error);
    }
}

// Chame a função para realizar a busca
buscarUsuario();

Além disso, você consegue usar parâmetros de modo a criar filtros no método findOne, observe como isso pode ser feito:

// Define uma função para buscar um único usuário com filtro
async function buscarUsuarioPorId(id) {
    try {
        // Busca um usuário com base no ID fornecido
        const usuario = await Usuarios.findOne({
            where: { id } // Define a condição de busca
        });
        
        if (usuario) {
            console.log('Usuário encontrado:', usuario.toJSON());
        } else {
            console.log('Nenhum usuário encontrado para o ID:', id);
        }
    } catch (error) {
        console.error('Erro ao buscar o usuário:', error);
    }
}

// Chame a função passando um ID como parâmetro
buscarUsuarioPorId(1); // Exemplo: buscar usuário com ID 1

Caso desejar, você pode buscar por um determinado e-mail:

where: { email: 'email@example.com' }

Ou diversas outras condições, por exemplo:

where: {
    email: 'email@example.com',
    ativo: true
}

Definindo um LIMIT, OFFSET e ORDER com Sequelize

No SQL, usamos o comando LIMIT para limitar a quantidade de dados que queremos retornar em uma consulta.

No Sequelize seu uso também é possível, veja como isso é feito:

// Define uma função para buscar usuários com um limite
async function buscarUsuariosComLimite(limite) {
    try {
        // Busca usuários com o limite especificado
        const usuarios = await Usuarios.findAll({
            limit: limite // Define o número máximo de usuários a serem retornados
        });

        console.log('Usuários encontrados:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários:', error);
    }
}

// Chame a função passando um limite como parâmetro
buscarUsuariosComLimite(5); // Exemplo: buscar 5 usuários

Além do LIMIT, temos o OFFSET, que é bastante usado para trazer os dados de forma paginada:

// Define uma função para buscar usuários com paginação
async function buscarUsuariosPaginados(page = 1, pageSize = 10) {
    try {
        // Calcula o offset (pula os registros das páginas anteriores)
        const offset = (page - 1) * pageSize;

        // Busca os usuários com limite e offset
        const usuarios = await Usuarios.findAll({
            limit: pageSize,  // Quantidade de registros por página
            offset: offset,   // Pular registros com base na página
            order: [['id', 'ASC']] // Ordenação (opcional)
        });

        console.log(`Usuários encontrados na página ${page}:`, usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários paginados:', error);
    }
}

// Chame a função passando o número da página e o tamanho da página
buscarUsuariosPaginados(2, 5); // Exemplo: buscar página 2 com 5 usuários por página

Note que usamos mais um parâmetro chamado de ORDER, que faz a ordenação dos registros pelo id em formato ASC (ascendente).

Especificando o nome das colunas que você quer retornar

Nem sempre em todas as consultas que realizarmos em nossas tabelas, precisamos retornar todos os campos.

As vezes só, só precisamos , não é verdade? 🙂

No caso do Sequelize, isso pode ser feito por meio da chave attributes que pode ser especificada dentro do método findAll(), observe a sua aplicação:

async function buscarNomeESenhaDosUsuarios() {
    try {
        const usuarios = await Usuarios.findAll({
            attributes: ['nome', 'senha'] // Especifica as colunas que você quer no SELECT
        });

        console.log('Usuários encontrados (nome e senha):', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar nome e senha dos usuários:', error);
    }
}

// Chame a função para realizar a busca
buscarNomeESenhaDosUsuarios();

No comando acima, estamos realizando a sequinte consulta do SQL:

SELECT `nome`, `senha` FROM `Usuarios`;

Além disso, você também pode incluir algumas condições em conjunto com o WHERE:

async function buscarNomeESenhaComFiltro() {
    try {
        const usuarios = await Usuarios.findAll({
            attributes: ['nome', 'senha'], // Seleciona apenas nome e senha
            where: {
                nome: 'João Silva' // Filtra pelo nome
            }
        });

        console.log('Usuários encontrados (nome e senha):', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar nome e senha dos usuários com filtro:', error);
    }
}

buscarNomeESenhaComFiltro();

Note que a chave where e attributes estão no mesmo nível de aninhamento 🙂

Operadores do Sequelize (que podemos usar com o WHERE)

Talvez você esteja se perguntando: "Legal, mas como eu faço para realizar um WHERE em conjunto com os operadores de negação, maior, menor, maior ou igual, menor ou igual e afins...?".

No caso do Sequelize, ele oferece suporte ao uso desses operadores (==, !=, >=, >, <, <=) por meio de um objeto chamado de Op.

Para usá-lo, você precisa ser importá-lo para dentro do arquivo que fará as requisições na sua tabela:

const { Op } = require('sequelize'); // Importa os operadores do Sequelize

Em seguida, dentro do objeto WHERE, mais especificamente dentro da chave que representa uma coluna, você vai precisar declará-lo da seguinte forma:

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.ne]: 'João Silva' // Verifica onde nome é != 'João Silva'
    }
  }
});

No caso do comando [Op.ne], ele verifica se o campo (nome) é diferente de um determinado valor. Ou seja, é equivalente ao operador de negação (!=).

Vejamos uma lista completa de outros operadores que você pode usar nas suas consultas 😋

Operador de Igual (= ou ==): Usamos o comando Op.eq para verificar se um campo é igual a um determinado valor:

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.eq]: 'João Silva' // Verifica se a coluna 'nome' é igual a 'João Silva'
    }
  }
});

Lembrando que uma consulta direta (sem o Op.eq) como vinhamos fazendo anteriormente, nada mais é do que um operador de igualdade (=).

Operador maior que (>): Usamos o comando Op.gt para verificar se um campo é maior que um determinado valor:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.gt]: 10 // Verifica se a coluna 'id' é maior que 10
    }
  }
});

Operador menor que (<): Usamos o comando Op.lt para verificar se um campo é menor que um determinado valor:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.lt]: 10 // Verifica se a coluna 'id' é menor que 10
    }
  }
});

Operador maior ou igual (>=): Usamos o comando Op.gte para verificar se um campo é maior ou igual que um determinado valor:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.gte]: 10 // Verifica se a coluna 'id' é maior ou igual a 10
    }
  }
});

Operador menor ou igual (<=): Usamos o comando Op.lte para verificar se um campo é menor ou igual que um determinado valor:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.lte]: 10 // Verifica se a coluna 'id' é menor ou igual a 10
    }
  }
});

Operador Entre (BETWEEN): Usamos o comando Op.between para verificar se um campo está entre dois valores:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.between]: [10, 20] // Verifica se a coluna 'id' está entre 10 e 20
    }
  }
});

Operador Fora do Intervalo (NOT BETWEEN): Usamos o comando Op.notBetween para verificar se um campo está fora do intervalo entre dois valores:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.notBetween]: [10, 20] // Verifica se a coluna 'id' não está entre 10 a 20
    }
  }
});

Operador Incluir (IN): Usamos o comando Op.in para verificar se um campo está dentro de uma lista de valores:

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.in]: ['João', 'Micilini', 'Carlos'] // Verifica se a coluna 'nome' possui os valores 'João', 'Carlos' ou 'Micilini'
    }
  }
});

Operador Não Incluir (NOT IN): Usamos o comando Op.notIn para verificar se um campo não está contido dentro da lista de valores:

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.notIn]: ['João', 'Micilini', 'Carlos'] // Verifica se a coluna 'nome' NÃO possui os valores 'João', 'Carlos' ou 'Micilini'
    }
  }
});

Operador Nulo (IS NULL): Usamos o comando Op.is para verificar se um campo é nulo:

const usuarios = await Usuarios.findAll({
  where: {
    senha: {
      [Op.is]: null // Verifica se a coluna 'senha' está vazia (nula)
    }
  }
});

Operador Não Nulo (IS NOT NULL): Usamos o comando Op.not para verificar se um campo não é nulo:

const usuarios = await Usuarios.findAll({
  where: {
    senha: {
      [Op.not]: null // Verifica se a coluna 'senha' NÃO está vazia (preenchida)
    }
  }
});

Operador Like (LIKE): Usamos o comando Op.like para verificar se um campo corresponde a um padrão, usado em buscas com coringas (%):

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.like]: '%João%' // Verifica se a coluna 'nome' atende a determinado padrão
    }
  }
});

Operador Not Like (NOT LIKE): Usamos o comando Op.notLike para verificar se um campo não corresponde a um padrão:

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.notLike]: '%João%' // Verifica se a coluna 'nome' NÃO atende a determinado padrão
    }
  }
});

Operador ILike (NOT LIKE '%valor%'): Usamos o comando Op.iLike para verificar um caso sensível (no PostgreSQL ele funciona de forma similar ao LIKE, mas case-insensitive):

const usuarios = await Usuarios.findAll({
  where: {
    nome: {
      [Op.iLike]: '%João%' // Verifica se a coluna 'nome' atende os requisitos em case-insensitive
    }
  }
});

Operador Any (ANY): Usamos o comando Op.any para verificar se qualquer valor corresponde aos valores inseridos dentro de um array:

const usuarios = await Usuarios.findAll({
  where: {
    id: {
      [Op.any]: [12, 99, 108] // Verifica se a coluna 'id' atende os requisitos listados no array.
    }
  }
});

Comparação Entre Colunas: Caso você queria comprar valores com outra coluna, basta usar o comando Op.col.

Vamos imaginar que você tenha uma tabela Usuarios com as colunas idade e idade_minima_permitida, e você quer buscar todos os usuários cuja idade sejam maiores ou iguais à idade_minima_permitida.

Para atingir este objetivo, você poderia usar o comando Op.col da seguinte forma:

const { Sequelize, DataTypes, Op } = require('sequelize');
const connection = require('../db/connection');

// Modelo Usuarios
const Usuarios = connection.define('Usuarios', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    idade: {
        type: DataTypes.INTEGER,
        allowNull: false
    },
    idade_minima_permitida: {
        type: DataTypes.INTEGER,
        allowNull: false
    }
}, {
    timestamps: true // Mantém createdAt e updatedAt
});

module.exports = Usuarios;
async function buscarUsuariosComIdadeValida() {
    try {
        // Busca usuários cuja idade é maior ou igual à idade_minima_permitida
        const usuarios = await Usuarios.findAll({
            where: {
                idade: {
                    [Op.gte]: Sequelize.col('idade_minima_permitida') // Compara coluna idade com idade_minima_permitida
                }
            }
        });

        console.log('Usuários encontrados:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários:', error);
    }
}

// Chame a função para realizar a busca
buscarUsuariosComIdadeValida();

Uso de JOIN no Sequelize

No SQL nos usamos o comando JOIN para fazer uma busca unificada usando duas ou mais colunas de diferentes tabelas.

Como no Sequelize a ideia é não usarmos consultas SQL de forma direta, o uso do JOIN se dá de uma maneira um pouco diferente (para alguns, a sua implementação é considerada um pouco mais trabalhosa).

Aqui nós precisamos fazer o uso dos modelos de associação hasOne, hasMany, belongsTo e belongsToMany para realizar os JOINs em nossas tabelas.

hasOne: define uma relação de "um para um". Exemplo: um usuário tem um perfil.

belongsTo: define uma relação de "muitos para um". Exemplo: um perfil pertence a um usuário.

hasMany: define uma relação de "um para muitos". Exemplo: um usuário tem muitos posts.

belongsToMany: define uma relação de "muitos para muitos". Exemplo: um post pode ter muitas tags, e uma tag pode ter muitos posts.

Tais modelos devem ser usados tanto durante a declaração de um Model, quanto durante a lógica de CRUD.

Usando o método hasOne com belongsTo

No exemplo de JOIN a seguir ("um para um"), precisamos ter dois Models, um chamado Usuarios e outro chamado Perfis.

Veja como eu separei a estrutura de pastas e arquivos:

- models/
   - Usuarios.js
   - Perfis.js
- db/
   - connection.js
- index.js (ou app.js, etc.)

models > Usuarios.js:

const { DataTypes } = require('sequelize');
const connection = require('../db/connection');

const Usuarios = connection.define('Usuarios', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: true
    }
}, {
    timestamps: true
});

module.exports = Usuarios;

models > Perfis.js:

const { DataTypes } = require('sequelize');
const connection = require('../db/connection');
const Usuarios = require('./Usuarios'); // Importar o modelo de Usuarios para a associação

const Perfis = connection.define('Perfis', {
    bio: {
        type: DataTypes.STRING,
        allowNull: true
    },
    avatar_url: {
        type: DataTypes.STRING,
        allowNull: true
    }
}, {
    timestamps: true
});

// Definindo a associação
Perfis.belongsTo(Usuarios, { foreignKey: 'usuarioId' });
Usuarios.hasOne(Perfis, { foreignKey: 'usuarioId' });

module.exports = Perfis;

Note que definimos uma associação dentro do arquivo Perfis.js, por meio dos métodos hasOne e belongsTo.

Usuarios.hasOne(Perfis): cria a associação entre Usuarios e Perfis e o Sequelize vai gerar uma coluna usuarioId em Perfis para armazenar a referência ao id de Usuarios.

Perfis.belongsTo(Usuarios): define que um perfil pertence a um usuário.

Para fazer uma consulta com JOIN, você pode fazer o uso do atributo include da seguinte forma durante a execução de busca dentro do seu index.js:

//Não se esqueça de importar o model de Usuarios e Perfis no documento!

async function buscarUsuariosComPerfis() {
    try {
        const usuarios = await Usuarios.findAll({
            include: {
                model: Perfis, // Faz um JOIN com o modelo Perfis
                as: 'Perfil'   // Alias para o JOIN, opcional
            }
        });

        console.log('Usuários com Perfis:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários com perfis:', error);
    }
}

// Chame a função para realizar a busca com JOIN
buscarUsuariosComPerfis();

include: { model: Perfis, as: 'Perfil' }: faz o JOIN com a tabela Perfis e retorna o perfil associado a cada usuário.

O código acima, executa a seguinte query SQL:

SELECT `Usuarios`.`id`, `Usuarios`.`nome`, `Usuarios`.`email`, `Perfis`.`bio`, `Perfis`.`avatar_url`
FROM `Usuarios`
LEFT OUTER JOIN `Perfis` ON `Usuarios`.`id` = `Perfis`.`usuarioId`;

Usando o método hasMany com belongsTo

Se você precisar executar uma relação de "um para muitos", você pode usar hasMany e belongsTo.

Para testarmos isso, vamos adicionar uma outra tabela chamada de Posts, onde um único usuário pode ter muitos posts publicados:

models > Posts.js:

// Modelo Posts
const Posts = connection.define('Posts', {
    titulo: {
        type: DataTypes.STRING,
        allowNull: false
    },
    conteudo: {
        type: DataTypes.TEXT,
        allowNull: false
    }
}, {
    timestamps: true
});

// Relacionamentos
Usuarios.hasMany(Posts, { foreignKey: 'usuarioId' }); // Um usuário pode ter muitos posts
Posts.belongsTo(Usuarios, { foreignKey: 'usuarioId' }); // Um post pertence a um usuário

Já no index.js, basta correlacionar da seguinte forma:

//Não se esqueça de importar o model de Usuarios e Posts no documento!

async function buscarUsuariosComPosts() {
    try {
        const usuarios = await Usuarios.findAll({
            include: {
                model: Posts, // Faz o JOIN com Posts
                as: 'Posts'   // Alias para o JOIN, opcional
            }
        });

        console.log('Usuários com Posts:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários com posts:', error);
    }
}

buscarUsuariosComPosts();

Usuarios.hasMany(Posts): um usuário pode ter muitos posts.

Posts.belongsTo(Usuarios): cada post pertence a um usuário.

include: { model: Posts, as: 'Posts' }: este comando faz o JOIN, e retorna os posts associados a cada usuário.

O código acima, executa a seguinte query SQL:

SELECT `Usuarios`.`id`, `Usuarios`.`nome`, `Posts`.`titulo`, `Posts`.`conteudo`
FROM `Usuarios`
LEFT OUTER JOIN `Posts` ON `Usuarios`.`id` = `Posts`.`usuarioId`;

Usando o método belongToMany

Se você quiser fazer um relacionamento "muitos para muitos", pode usar belongsToMany com uma tabela intermediária.

Por exemplo, Usuarios e Projetos podem ter um relacionamento "muitos para muitos", uma vez que um usuário pode participar de vários projetos, e um projeto pode ter vários usuários.

Para exemplificar isso, vamos criar mais um model chamado de Projetos.

models > Projetos.js:

// Modelo Projetos
const Projetos = connection.define('Projetos', {
    nome: {
        type: DataTypes.STRING,
        allowNull: false
    }
}, {
    timestamps: true
});

// Relacionamento muitos para muitos
Usuarios.belongsToMany(Projetos, { through: 'UsuarioProjetos' }); // Tabela intermediária UsuarioProjetos
Projetos.belongsToMany(Usuarios, { through: 'UsuarioProjetos' });

No index.js, você pode buscar os usuários e seus projetos associados com o seguinte comando:

//Não se esqueça de importar o model de Usuarios e Projetos no documento!

async function buscarUsuariosComProjetos() {
    try {
        const usuarios = await Usuarios.findAll({
            include: {
                model: Projetos, // Faz o JOIN com Projetos
                as: 'Projetos'   // Alias para o JOIN, opcional
            }
        });

        console.log('Usuários com Projetos:', usuarios.map(usuario => usuario.toJSON()));
    } catch (error) {
        console.error('Erro ao buscar usuários com projetos:', error);
    }
}

buscarUsuariosComProjetos();

Os comandos apresentados acima seriam equivalente a seguinte consulta SQL:

SELECT 
    `Usuarios`.`id`, 
    `Usuarios`.`nome`, 
    `Usuarios`.`email`, 
    `Projetos`.`id` AS `Projetos.id`, 
    `Projetos`.`nome` AS `Projetos.nome` 
FROM 
    `Usuarios` AS `Usuarios` 
INNER JOIN 
    `UsuarioProjetos` AS `UsuarioProjetos` 
    ON `Usuarios`.`id` = `UsuarioProjetos`.`UsuarioId` 
INNER JOIN 
    `Projetos` AS `Projetos` 
    ON `UsuarioProjetos`.`ProjetoId` = `Projetos`.`id`;

Atualizando dados da tabela

Para atualizarmos um determinado dado na nossa tabela, podemos fazer o uso do método findByPk, onde passamos por parâmetro o valor que queremos buscar.

Em seguida, declaramos os objetos dentro de uma estrutura de chave e valor que nós queremos atualizar.

Por fim, usamos o método save para realizar o update, vejamos como isso pode ser feito:

const { Sequelize, DataTypes } = require('sequelize');

const connection = require('./db/connection'); // Ajuste o caminho conforme necessário
const Usuarios = require('./models/Usuarios'); // Ajuste o caminho conforme necessário

//Cria as tabelas no banco de dados caso não existam...
connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

//Define uma função para atualizar um usuário
async function atualizarUsuario() {
    try {
        // Busca um usuário pelo ID
        const usuario = await Usuarios.findByPk(1);

        if (usuario) {
            // Atualiza o nome do usuário
            usuario.nome = 'João da Silva Neto Costa';
            await usuario.save();

            console.log('Usuário atualizado com sucesso:', usuario.toJSON());
        } else {
            console.error('Usuário não encontrado!');
        }
    } catch (error) {
        console.error('Erro ao atualizar usuário:', error);
    }
}

// Chame a função para realizar a atualização
atualizarUsuario();

Veja o resultado final no console:

O método findByPk vai buscar o usuário cujo o id é igual a 1, e então, fazer as devidas atualizações.

Mas nem sempre queremos filtrar apenas pelo id do usuário, por vezes, queremos fazer uma checagem de id com outra coluna, veja abaixo um exemplo de como isso pode ser feito:

//Define um afunção para atualizar um usuário cujo o id seja 2 e o nome seja 'Maria'
async function atualizarUsuarioComFiltro() {
    try {
        // Atualiza o nome do usuário com ID 2
        const [rowsUpdated, [usuario]] = await Usuarios.update({
            nome: 'Maria da Silva',//valores que serão atualizados
        }, {
            where: {//Filtro para atualização
                id: 2,
                nome: 'Maria'
            },
            returning: true//Retorna os dados que foram atualizados
        });

        if (rowsUpdated > 0) {
            console.log('Usuário atualizado com sucesso:', usuario.toJSON());
        } else {
            console.error('Usuário não encontrado!');
        }
    } catch (error) {
        console.error('Erro ao atualizar usuário:', error);
    }
}

// Chame a função para realizar a atualização com filtro
atualizarUsuarioComFiltro();

No comando acima, estamos fazendo o uso do método update (que é mais completo que o findByPk) para atualizar os dados usando Sequelize.

Removendo dados da tabela

Para remover um determinado registro da tabela, você tanto pode usar o método findByPk quanto o método destroy.

No caso do método findByPk, primeiro ele vai buscar um determinado dado cujo id é igual ao valor passado por parâmetro para dentro do método:

const { Sequelize, DataTypes } = require('sequelize');

const connection = require('./db/connection'); // Ajuste o caminho conforme necessário
const Usuarios = require('./models/Usuarios'); // Ajuste o caminho conforme necessário

//Cria as tabelas no banco de dados caso não existam...
connection.sync().then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

//Define uma função para deletar um usuário
async function deletarUsuario() {
    try {
        // Busca um usuário pelo ID
        const usuario = await Usuarios.findByPk(1);

        if (usuario) {
            // Deleta o usuário
            await usuario.destroy();

            console.log('Usuário deletado com sucesso!');
        } else {
            console.error('Usuário não encontrado!');
        }
    } catch (error) {
        console.error('Erro ao deletar usuário:', error);
    }
}

// Chame a função para realizar a exclusão
deletarUsuario();

E quando ele achar esse registro, basta executar o método destroy para excluí-lo.

No caso do método destroy, nós temos um controle maior sobre a nossa deleção, onde podemos inserir a claúsula WHERE em conjunto:

//Define uma função para deletar um usuário cujo o id seja 2 e o nome seja 'Maria'
async function deletarUsuarioComFiltro() {
    try {
        // Deleta o usuário com ID 2 e nome 'Maria'
        const rowsDeleted = await Usuarios.destroy({
            where: {//Filtro para exclusão
                id: 2,
                nome: 'Maria'
            }
        });

        if (rowsDeleted > 0) {
            console.log('Usuário deletado com sucesso!');
        } else {
            console.error('Usuário não encontrado!');
        }
    } catch (error) {
        console.error('Erro ao deletar usuário:', error);
    }
}

// Chame a função para realizar a exclusão com filtro
deletarUsuarioComFiltro();

Forçando a recriação das tabelas no seu banco de dados

Em tópicos anteriores, você aprendeu a utilizar o método sync do Sequelize para garantir que todas as tabelas necessárias para o seu projeto, estejam criadas antes do NodeJS rodar o projeto em sí.

Entrentato, também é possível forçar a recriação de todas as tabelas do seu banco de dados, o que ocasionaria (também) a perde dos seus dados.

Para habilitar essa opção, basta passar o atributo force como true dentro do método sync, por exemplo:

const connection = require('./db/connection');
const usuarios = require('./models/Usuarios');

connection.sync({ force: true }).then(() => {
    console.log('Tabelas Criadas com Sucesso!');
}).catch((error) => {
    console.error('Erro ao criar as Tabelas:', error);
});

console.log('Servidor Iniciado com Sucesso!');

Basicamente o comando force, executa um DROP TABLE de todas as suas tabelas, e as recria novamente do zero (e sem os dados, é claro rs).

Arquivos da lição

Os arquivos que você viu durante o decorrer desta lição, podem ser encontrados neste link.

Conclusão

Nesta lição, você aprendeu diversos conceitos sobre o uso do ORM mais famoso do NodeJS, o SEQUELIZE.

Aqui você aprendeu a instalá-lo no seu projeto, além de executar todas as operações de CRUD com filtros.

Sinta-se a vontade para consultar este conteúdo sempre que quiser, e até a próxima lição 🙃

Criadores de Conteúdo

Foto do William Lima
William Lima
Fundador da Micilini

Inventor nato, escreve conteudos de programação para o portal da micilini.

Torne-se um MIC 🤖

Mais de 100 mic's já estão conectados na plataforma.