
Construindo uma API Node Express com Sequelize ORM
Requisitos
Node.js instalado. Caso não possua o node intalado ainda clique aqui
Criando aplicação e instalando as dependências
Vamos iniciar uma aplicação com o comando npm init -y
e criar na pasta raiz os arquivos server.js, bd.js e quatro novas pastas chamadas router, controller, model e service.
Agora podemos instalar as dependências do projeto através do comando npm install express dotenv pg postgres sequelize sequelize-cli cors bcrypt
.
Instale também como dependencias de desenvolvimento npm install nodemon morgan --save-dev
.
Configure também um comando de inicialização da aplicação atrelado ao nodemon no package.json
Ao final do processo você deve ter a seguinte estrutura de arquivo e package.json.

{
"name": "apiexemplo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start":"nodemon start server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"pg": "^8.11.3",
"postgres": "^3.4.0",
"sequelize": "^6.33.0",
"sequelize-cli": "^6.6.1"
},
"devDependencies": {
"morgan": "^1.10.0",
"nodemon": "^3.0.1"
}
}
Banco de Dados
Crie um banco de dados local ou então siga op tutorial abaixo para criar uma instância do Elephant SQL.
Agora vamos prosseguir com a configuração do banco no arquivo bd.js que criamos anteriormente. Importe o sequelize e do dotenv conforme mostrado. Crie também na pasta raiz do projeto um arquivo .env. Nesta etapa vamos utilizar a url copiada por você na última etapa do tutorial acima.
const Sequelize = require("sequelize"); //importação do sequelize
require("dotenv").config(); //importação do .env com as variáveis de ambiente
const bdUser = process.env.user; //variavel de ambiente
const bdPassword = process.env.password; //variavel de ambiente
const sequelize = new Sequelize(
`postgres://${bdUser}:${bdPassword}@silly.db.elephantsql.com/${bdUser}`, // sua url de conexão com o banco
{ dialect: "postgres" } //define o dialeto do banco
);
module.exports = sequelize; //exporte padrão do Node.
#Dados BD
user="seuUsuario"
password="suaSenha"
Configurando o servidor e definindp uma porta
Agora podemos configurar o servidor em server.js
const express = require("express");
const cors = require("cors");
const morgan = require("morgan");
const bd = require("./bd");
const app = express(); //inicia o express
app.use(morgan("dev")); //imprime os logs das operações durante o desenvolvimento
app.use(cors()); //libera o cross origin
app.use(express.json()); // configura a API para receber e enviar json (Headers HTTP)
//rota padrão na qual podemos verificar se o servidor está disponível
app.get("/", async (req, res) => {
res.status(200).json({ msg: "Api ok!" });
});
//inicia o servidor através de uma self-invoked function que executa na inicialização da aplicação
(async () => {
try {
await bd.sync({ force: false });// sincroniza o banco
console.log("Banco conectado!");
app.listen(3000, () => console.log("Service is live!")); //iniciar efetivamente o servidor na porta 3000
} catch (err) {
console.log(err); //imprime o erro caso este ocorra
}
})();
Observe o objeto passado de parâmetro na função que sincroniza o banco.
force: true
: Quando você defineforce
comotrue
, a sincronização do banco de dados recriará todas as tabelas, ou seja, ele irá descartar todas as tabelas existentes e criá-las novamente do zero. Isso é útil em situações de desenvolvimento ou testes, quando você deseja recriar o esquema do banco de dados para refletir as mudanças no seu modelo de dados ou quando deseja começar com um banco de dados vazio.force: false
: Ao definirforce
comofalse
, a sincronização do banco de dados não irá recriar as tabelas se elas já existirem. Isso é a configuração padrão e é mais apropriado para ambientes de produção, onde você deseja manter os dados existentes. Usarfalse
é uma abordagem mais segura para evitar a perda de dados acidental, pois não apagará ou recriará tabelas existentes.
Em resumo, ao definir force: true
, você está instruindo o sistema a recriar o esquema do banco de dados do zero, enquanto force: false
garante que o esquema atual do banco de dados seja mantido, evitando a perda de dados. A escolha entre essas opções dependerá dos requisitos do seu ambiente, sendo true
mais comum durante o desenvolvimento e false
mais adequado para produção.
Podemos agora testar a conexão com o banco de dados iniciando a aplicação através do comando npm start
. Se a conexão com o banco for bem sucedida a mensagem de "Banco conectado” será exibida no console bem como “Service is live!”que indica que o servidor está rodando.
mackleaps@222816-MackFCI apiExemplo % npm start
> apiexemplo@1.0.0 start
> nodemon start server.js
[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node start server.js`
Executing (default): SELECT 1+1 AS result
Banco conectado!
Service is live!
Também podemos verificar que a aplicação foi iniciada corretamente através da rota que definimos para esta finalidade no server. Em seu navegador use a url http://localhot:3000 e verifique se a mensagem {msg:Api ok!} foi exibida.


Criando um Schema com Sequelize
Neste passo vamos criar o esquema para a tabela usuários de nossa aplicação. Crie um arquivo chamado userModel.js dentro da pasta model que criamos anteriormente.
const sequelize = require("sequelize"); // importamos o sequelize que gerencia a criação da tabela
const db = require("../bd"); //importamos o banco no qual conectamos anteriormente
const User = db.define(
"usuarios", //define o nome da tabela no banco de dados
{
id: {
type: sequelize.INTEGER.UNSIGNED,
primaryKey: true, //define que esta coluna é a chame primária
autoIncrement: true, //incrmenta o id em uma unidade evitando duplicidade de chave primaria
allowNull: false, //define que este atributo não pode ser vazio
},
nome: {
type: sequelize.STRING,
allowNull: false,
unique: true, //define que todos os valores desta coluna deve ser único
},
password: {
type: sequelize.STRING,
allowNull: false,
},
permissao: {
type: sequelize.STRING,
allowNull: false,
},
},
{
timestamps: false, //desabilita o armazenamento de datas em que foi criado e atualizado
}
);
module.exports = User;
Para entender melhor o funcionamento e os tipos de dados acesse a documentação do sequelize no link abaixo.
O sequelize também permite definir relacionamento entre as tabelas.
Criando o CRUD da API através de funções controller
Agora vamos criar as funções que são responsáveis por receber as requisições processa-las recuperando as informações do banco de dados e retornar um json como resposta para o cliente. Crie um arquivo userController.js na pasta controller.
Geralmente, um arquivo de controlador é responsável por conter todas as funções que manipulam as operações de CRUD (Create, Read, Update, Delete) lidando diretamente com as operações HTTP para um determinado modelo de dados em um aplicativo. Neste exemplo, o foco está em operações de usuário usando o modelo User
.
recuperarUsuarios
: Essa função lida com a operação de leitura para recuperar todos os usuários da tabela. Ela usa o métodoUser.findAll()
para buscar todos os registros e responde com os usuários encontrados ou uma mensagem de erro.adicionarUsuario
: Essa função lida com a operação de criação para adicionar um novo usuário à tabela. Ela desestrutura os dados do corpo da requisição, usa o métodoUser.create()
para criar um novo registro e responde com o novo usuário ou uma mensagem de erro.findById
: Esta função trata da operação de leitura para encontrar um usuário específico com base no ID. Ela extrai o ID da URL da rota, usa o métodoUser.findByPk(id)
para buscar o registro correspondente e responde com o usuário encontrado ou uma mensagem de erro.excluiUsuario
: Essa função lida com a operação de exclusão para remover um usuário com base no ID. Ela extrai o ID da URL da rota, usa o métodoUser.destroy()
para excluir o registro e responde com uma mensagem de sucesso ou uma mensagem de erro.atualizarUsuario
: Esta função trata da operação de atualização para modificar um usuário com base no ID. Ela extrai o ID da URL da rota e os novos dados do corpo da requisição. Ela também inicia uma transação para garantir a integridade do banco de dados, atualiza o registro com os novos dados e responde com uma mensagem de sucesso ou uma mensagem de erro.
Por fim, as funções são exportadas como um objeto no final do arquivo, tornando-as disponíveis para uso em outras partes do aplicativo.
const User = require("../model/userModel"); //import do model de user que criamos anteiormente
const sequelize = require("../bd"); //importe do sequelize para definir controle de transações (Transaction Control Language, TCL)
//req-> paramentro que recebe a requisição
//res ->parametro que configura a resposta da requisição
//READ->GET
async function recuperarUsuarios(req, res) {
try {
const usuarios = await User.findAll(); //método findAll do sequelize retorna um sequelize object com todos os usuários do banco
//verifica se usuarios for vazio retorna 404 not found como resposta
if (usuarios.length === 0) {
res.status(404).json({ msg: "Não foram encontados usuários" });
return;
}
res.status(200).json(usuarios); //se a busca for bem sucedida retorna 200 e um array que contém json com as informações dos usuários
} catch (err) {
//tratamento de excessões
console.log(err);
res.status(500).json({ msg: "Falha ao recuperar usuários!" }); //caso algum erro ocorra retorna internal server error 500 junto da mensagem de erro
}
}
//CREATE->POST
async function adicionarUsuario(req, res) {
const { nome, password, permissao } = req.body; //desestruturação do objeto presente no corpo da requisição
try {
//recebe json como argumento do novo elemento que será criado e retorna elemento que foi criado
const novoUsuario = await User.create({
nome: nome,
password: password,
permissao: permissao,
});
res.status(201).json(novoUsuario); //retorna para o cliente código 201 created e o json do novo elemento
} catch (err) {
console.log(err);
res.status(500).json({ msg: "Falha ao criar usuário!" }); //retorna internal server error 500 caso ocorra algum erro durante a criação
}
}
//READ->GET
async function findById(req, res) {
const { id } = req.params; //desestrutura a url da rota recuperando o id
try {
const usuario = await User.findByPk(id); // findByPk é um método do sequelize que pesquisa e retorna um objeto que corresponde aquela chave primária
if (!usuario) {
//verifica se usuario for vazio retorna 404 not found como resposta
res.status(404).json({ msg: "Usuario não encontrado" });
return;
}
res.status(200).json(usuario); //se a busca for bem sucedida retorna 200 e um json com as informações do usuário
} catch (err) {
res.status(500).json({ msg: "Falha ao criar usuário!" }); //retorna internal server error 500 caso ocorra algum erro durante a operação
}
}
//DELETE->DELETE
async function excluiUsuario(req, res) {
const { id } = req.params; //desestrutura a url da rota recuperando o id do elemnto a ser excluido
try {
//exclui a linha da tabela através do id passado na uri através do método destroy do sequelize
await User.destroy({
where: {
id: id,
},
});
res.status(200).json({ msg: "Usuário excluido!" });
} catch (err) {
console.log(err);
res.status(500).json({ msg: "Falha ao criar usuário!" }); //retorna internal server error 500 caso ocorra algum erro durante a operação
}
}
//UPDATE->PUT
async function atulizarUsuario(req, res) {
const { id } = req.params; //desestrutura a url da rota recuperando o id do elemnto a ser atualizado
const { novoNome, novoPassword, novaPermissao } = req.body; //desestruturação do objeto presente no corpo da requisição
const t = await sequelize.transaction(); //abre uma transação para evitar que o banco perda sua integridade
try {
//update é metodo do sequelize que atualiza linha da tabela
await User.update(
{
nome: novoNome,
password: novoPassword,
permissao: novaPermissao,
},
{
where: { id: id },
transaction: t, // Associa a transação à atualização
}
);
// Confirme a transação
await t.commit();
res.status(200).json({ msg: "Usuário atualizado!" });
} catch (err) {
// Em caso de erro, reverta a transação
await t.rollback();
res.status(500).json({ msg: "Falha ao atualizar" });
}
}
//Exportação das funções para acesso no router
module.exports = {
recuperarUsuarios,
adicionarUsuario,
findById,
excluiUsuario,
atulizarUsuario,
};
Definindo as rotas no Router
Em um aplicativo web, o roteamento desempenha um papel fundamental na definição de como as diferentes URLs são tratadas, quais ações devem ser executadas e como os recursos são servidos. Para facilitar essa funcionalidade em aplicativos Node.js, a biblioteca Express fornece um recurso chamado Router, que é uma parte essencial na criação de rotas e na manipulação de solicitações HTTP.
O que é um Router?
Um Router no contexto do Express é um módulo que permite organizar e mapear rotas em seu aplicativo Node.js. Ele funciona como um middleware que ajuda a direcionar solicitações HTTP para as funções de controle adequadas. Essas funções de controle executam a lógica de negócios necessária com base no caminho da URL e no método HTTP da solicitação.
O que um Router Define?
Um Router define como as rotas devem se comportar em relação a uma série de critérios, incluindo o método HTTP, a URL e qualquer parâmetro que possa estar presente na rota. Com um Router, você pode criar rotas para lidar com:
GET: Para buscar informações.
POST: Para criar novos recursos.
PUT: Para atualizar recursos existentes.
DELETE: Para excluir recursos.
Rotas Dinâmicas
No Express, você pode definir rotas dinâmicas usando notações especiais, como :id
. Essa notação indica que um valor variável será capturado a partir da URL e disponibilizado como um parâmetro na função de controle associada. Por exemplo, a rota /usuarios/:id
permitirá que você acesse o valor id
na função de controle quando um URL como /usuarios/123
for acessado, onde 123
é o valor do parâmetro id
.
A rota /produtos/:categoria?
no Express define um parâmetro chamado categoria
como opcional, permitindo que os clientes acessem a rota com ou sem um valor para categoria
. Quando um valor é fornecido, ele é acessível na função de controle através de req.params.categoria
, mas se nenhum valor for especificado na URL, o Express considera o valor padrão como "todos". Isso oferece flexibilidade ao usuário, tornando possível listar produtos de uma categoria específica quando desejado, ou todos os produtos quando a categoria não é especificada.
Agora podemos criar no diretório router o arquivo userRouter.js . Neste arquivo vamos mapear as funções do controller para as rotas e métodos HTTP.
As funções do controlador são importadas no início do arquivo
routes.js
.O módulo Express é importado para criar e configurar o objeto
Router
.Cada rota é definida usando o método correspondente (
get
,post
,delete
,put
) no objetoroutes
. A função do controlador correspondente é associada a cada rota.Nas rotas que exigem um parâmetro, como
/usuarios/:id
, a notação:id
indica que esse parâmetro será parte da URL da requisição. Por exemplo, uma solicitação para/usuarios/123
fornecerá o valor123
como o parâmetroid
.Finalmente, o objeto
routes
é exportado para que ele possa ser utilizado no arquivo principal (geralmenteserver.js
) para configuração do servidor Express.
const express = require("express"); //importação do express
const routes = express.Router(); //importação do Router da biblioeca express
//importação das funções do controller que serão mapeadas para as rotas
const {
recuperarUsuarios,
adicionarUsuario,
findById,
excluiUsuario,
atulizarUsuario,
} = require("../controller/userController");
//mapeamento das funções do controller para seus respectivos métodos HTTP e rotas
routes.get("/usuarios", recuperarUsuarios);
routes.post("/usuario", adicionarUsuario);
routes.get("/usuario/:id", findById); //notação :id indica que o parametro id sera informado na url da requisição
routes.delete("/usuario/:id", excluiUsuario); //notação :id indica que o parametro id sera informado na url da requisição
routes.put("/usuario/:id", atulizarUsuario); //notação :id indica que o parametro id sera informado na url da requisição
//exportamos o objeto routes
module.exports = routes;
Configurando as rotas no aplicativo principal
Para concluir esta seção do guia, vamos integrar as rotas que definimos no router ao servidor, tornando-as finalmente acessíveis para uso por aplicativos externos.
No arquivo server.js
, realize a importação do objeto routes
e, em seguida, passe esse objeto como argumento para a função app.use()
. Isso permitirá a integração das rotas definidas no arquivo de rotas com o aplicativo principal.
const express = require("express");
const cors = require("cors");
const morgan = require("morgan");
const bd = require("./bd");
//importamos as rotas do usuário
const userRoutes = require("./router/userRouter");
const app = express(); //inicia o express
app.use(morgan("dev")); //imprime os logs das operações durante o desenvolvimento
app.use(cors()); //libera o cross origin
app.use(express.json()); // configura a API para receber e enviar json.
app.use(userRoutes); // torna as rotas disponíveis para uso
//rota padrão na qual podemos verificar se o servidor está disponível
app.get("/", async (req, res) => {
res.status(200).json({ msg: "Api ok!" });
});
//inicia o servidor através de uma self-invoked function
(async () => {
try {
await bd.sync({ force: false }); // sincroniza o banco
console.log("Banco conectado!");
app.listen(3000, () => console.log("Service is live!")); //iniciar efetivamente o servidor na porta 3000
} catch (err) {
console.log(err);
}
})();
Agora execute novamente a aplicação através do comando npm start . Observe que o sequelize se encarregara da criação da tabela usuarios no banco.

Neste momento a API já está pronta para receber requisições.
Last updated