Criando uma API no Express com Node.js
Usando Express e Node.js, podemos implementar uma API completa no conceito REST para interagir com o usuário. A arquitetura REST propõe a aplicação dos seguintes métodos ou tipos de requisições HTTP para interagir com o servidor:
GET
: obter informações do servidorPOST
: enviar informações para o servidorPUT
: atualizar informações no servidorDELETE
: remover informações do servidor
O REST é especialmente conveniente ao criar qualquer tipo de Single Page Application. Vamos ver como criar nossa própria API. Para um novo projeto, vamos criar uma nova pasta. Em seguida, adicionaremos o pacote express ao projeto usando o comando:
npm install express
Neste caso, vamos criar um projeto experimental, que demonstrará a aplicação de REST em uma aplicação usando Node.js e Express. Para processar as requisições, vamos definir o seguinte arquivo app.js
:
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.static("public"));
// Base de dados condicional
const users = [];
let id = 1; // para definir identificadores
// Função auxiliar para encontrar o índice do usuário pelo id
function findUserIndexById(id) {
for (let i = 0; i < users.length; i++) {
if (users[i].id == id) return i;
}
return -1;
}
app.get("/api/users", function (_, res) {
res.send(users);
});
// Obtenção de um usuário pelo id
app.get("/api/users/:id", function (req, res) {
const id = req.params.id; // Obtendo o id
// Encontrando o usuário no array pelo id
const index = findUserIndexById(id);
// Enviando o usuário
if (index > -1) {
res.send(users[index]);
} else {
res.status(404).send("User not found");
}
});
// Recebendo dados enviados
app.post("/api/users", function (req, res) {
if (!req.body) return res.sendStatus(400);
const userName = req.body.name;
const userAge = req.body.age;
const user = { name: userName, age: userAge };
// Atribuindo o identificador da variável id e incrementando
user.id = id++;
// Adicionando o usuário ao array
users.push(user);
res.send(user);
});
// Removendo usuário pelo id
app.delete("/api/users/:id", function (req, res) {
const id = req.params.id;
const index = findUserIndexById(id);
if (index > -1) {
// Removendo o usuário do array pelo índice
const user = users.splice(index, 1)[0];
res.send(user);
} else {
res.status(404).send("User not found");
}
});
// Alterando usuário
app.put("/api/users", function (req, res) {
if (!req.body) return res.sendStatus(400);
const id = req.body.id;
const userName = req.body.name;
const userAge = req.body.age;
const index = findUserIndexById(id);
if (index > -1) {
// Alterando os dados do usuário
const user = users[index];
user.age = userAge;
user.name = userName;
res.send(user);
} else {
res.status(404).send("User not found");
}
});
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
Nesta aplicação, para simplificar, utilizaremos um array simples users
como um banco de dados. Por padrão, ele está vazio:
const users = [];
Cada objeto que será adicionado a este array terá um identificador numérico específico. Para definir os identificadores dos objetos ao criá-los, definimos a variável id
:
let id = 1;
Também precisaremos de uma função auxiliar para encontrar o índice de um usuário pelo id
:
function findUserIndexById(id) {
for (let i = 0; i < users.length; i++) {
if (users[i].id == id) return i;
}
return -1;
}
Se o usuário for encontrado, será retornado o índice dele. Se não for encontrado, será retornado o número -1.
Para processar as requisições, foram definidos cinco métodos para cada tipo de requisição: app.get()
, app.post()
, app.delete()
e app.put()
.
Quando a aplicação recebe uma requisição do tipo GET no endereço api/users
, é acionado o seguinte método:
app.get("/api/users", function (req, res) {
res.send(users);
});
Como resultado do processamento, devemos enviar o array de usuários usando o método res.send()
.
De maneira semelhante, funciona outro método app.get()
, que é acionado quando o id
do usuário é especificado no endereço:
app.get("/api/users/:id", function (req, res) {
const id = req.params.id; // Obtendo o id
// Encontrando o usuário no array pelo id
const index = findUserIndexById(id);
// Enviando o usuário
if (index > -1) {
res.send(users[index]);
} else {
res.status(404).send("User not found");
}
});
A única diferença neste caso é que precisamos encontrar o usuário desejado pelo id
no array. Se ele não for encontrado, retornamos o código de status 404: res.status(404).send()
.
Ao receber uma requisição do tipo POST, extraímos os dados enviados pelo cliente da seguinte maneira:
app.post("/api/users", function (req, res) {
if (!req.body) return res.sendStatus(400);
const userName = req.body.name;
const userAge = req.body.age;
const user = { name: userName, age: userAge };
// Atribuindo o identificador da variável id e incrementando
user.id = id++;
// Adicionando o usuário ao array
users.push(user);
res.send(user);
});
Como no início do arquivo configuramos o parsing automático para JSON na pipeline de processamento das requisições, o corpo da requisição será um objeto JSON, do qual podemos extrair as propriedades name
e age
para criar o objeto usuário. Para definir o identificador, usamos a variável global id
, cujo valor é incrementado.
Ao remover um usuário, realizamos ações semelhantes, mas agora extraímos do array o objeto que será removido:
// Removendo usuário pelo id
app.delete("/api/users/:id", function (req, res) {
const id = req.params.id;
const index = findUserIndexById(id);
if (index > -1) {
// Removendo o usuário do array pelo índice
const user = users.splice(index, 1)[0];
res.send(user);
} else {
res.status(404).send("User not found");
}
});
Se o usuário não for encontrado, retornamos o código de status 404.
Se precisarmos alterar um usuário, usamos o método app.put
:
app.put("/api/users", function (req, res) {
if (!req.body) return res.sendStatus(400);
const id = req.body.id;
const userName = req.body.name;
const userAge = req.body.age;
const index = findUserIndexById(id);
if (index > -1) {
// Alterando os dados do usuário
const user = users[index];
user.age = userAge;
user.name = userName;
res.send(user);
} else {
res.status(404).send("User not found");
}
});
Para buscar o objeto a ser alterado, encontramos seu índice pelo id
, obtemos o objeto pelo índice e modificamos suas propriedades.
Assim, definimos a API mais simples possível. Agora, vamos adicionar o código do cliente. Conforme estabelecido no código, o Express utiliza a pasta public
para armazenar arquivos estáticos. Portanto, criaremos essa pasta no projeto. Dentro dessa pasta, definiremos um novo arquivo index.html
, que atuará como cliente. No final, todo o projeto terá a seguinte estrutura:
meu-projeto │ ├── app.js │ ├── package.json │ ├── node_modules │ └── public └── index.html
Em seguida, definiremos o seguinte código no arquivo index.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Programício</title>
<style>
td,
th {
padding: 5px;
min-width: 90px;
max-width: 200px;
text-align: start;
}
.btn {
padding: 4px;
border: 1px solid #333;
background-color: #eee;
border-radius: 2px;
margin: 5px;
cursor: pointer;
}
</style>
</head>
<body>
<h2>Lista de Usuários</h2>
<form name="userForm">
<input type="hidden" name="id" value="0" />
<p>
<label>Nome:</label><br />
<input name="name" />
</p>
<p>
<label>Idade:</label><br />
<input name="age" type="number" />
</p>
<p>
<button id="submitBtn" type="submit">Salvar</button>
<button id="resetBtn">Resetar</button>
</p>
</form>
<table>
<thead>
<tr>
<th>Id</th>
<th>Nome</th>
<th>Idade</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
const tbody = document.querySelector("tbody");
// Obtenção de todos os usuários
async function getUsers() {
const response = await fetch("/api/users", {
method: "GET",
headers: { Accept: "application/json" },
});
if (response.ok === true) {
const users = await response.json();
users.forEach((user) => {
tbody.append(row(user));
});
}
}
// Obtenção de um usuário
async function getUser(id) {
const response = await fetch("/api/users/" + id, {
method: "GET",
headers: { Accept: "application/json" },
});
if (response.ok === true) {
const user = await response.json();
const form = document.forms["userForm"];
form.elements["id"].value = user.id;
form.elements["name"].value = user.name;
form.elements["age"].value = user.age;
}
}
// Adição de usuário
async function createUser(userName, userAge) {
const response = await fetch("api/users", {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({ name: userName, age: parseInt(userAge, 10) }),
});
if (response.ok === true) {
const user = await response.json();
reset();
tbody.append(row(user));
}
}
// Edição de usuário
async function editUser(userId, userName, userAge) {
const response = await fetch("api/users", {
method: "PUT",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({ id: userId, name: userName, age: parseInt(userAge, 10) }),
});
if (response.ok === true) {
const user = await response.json();
reset();
document.querySelector(`tr[data-rowid="${user.id}"]`).replaceWith(row(user));
}
}
// Remoção de usuário
async function deleteUser(id) {
const response = await fetch("/api/users/" + id, {
method: "DELETE",
headers: { Accept: "application/json" },
});
if (response.ok === true) {
const user = await response.json();
document.querySelector(`tr[data-rowid="${user.id}"]`).remove();
}
}
// Resetar formulário
function reset() {
const form = document.forms["userForm"];
form.reset();
form.elements["id"].value = 0;
}
// Criação de linha para a tabela
function row(user) {
const tr = document.createElement("tr");
tr.setAttribute("data-rowid", user.id);
const idTd = document.createElement("td");
idTd.append(user.id);
tr.append(idTd);
const nameTd = document.createElement("td");
nameTd.append(user.name);
tr.append(nameTd);
const ageTd = document.createElement("td");
ageTd.append(user.age);
tr.append(ageTd);
const linksTd = document.createElement("td");
const editLink = document.createElement("a");
editLink.setAttribute("data-id", user.id);
editLink.setAttribute("class", "btn");
editLink.append("Editar");
editLink.addEventListener("click", (e) => {
e.preventDefault();
getUser(user.id);
});
linksTd.append(editLink);
const removeLink = document.createElement("a");
removeLink.setAttribute("data-id", user.id);
removeLink.setAttribute("class", "btn");
removeLink.append("Deletar");
removeLink.addEventListener("click", (e) => {
e.preventDefault();
deleteUser(user.id);
});
linksTd.append(removeLink);
tr.appendChild(linksTd);
return tr;
}
// Resetar valores do formulário
document.getElementById("resetBtn").addEventListener("click", (e) => {
e.preventDefault();
reset();
});
// Envio do formulário
document.forms["userForm"].addEventListener("submit", (e) => {
e.preventDefault();
const form = document.forms["userForm"];
const id = form.elements["id"].value;
const name = form.elements["name"].value;
const age = form.elements["age"].value;
if (id == 0) createUser(name, age);
else editUser(id, name, age);
});
// Carregar usuários
getUsers();
</script>
</body>
</html>
A lógica principal está contida no código JavaScript. Ao carregar a página no navegador, todos os objetos são obtidos do banco de dados pela função getUsers
:
async function getUsers() {
// Envia a requisição e obtém a resposta
const response = await fetch("/api/users", {
method: "GET",
headers: { "Accept": "application/json" {
{);
// Se a requisição for bem-sucedida
if (response.ok === true) {
// Obtém os dados
const users = await response.json();
users.forEach(user => {
// Adiciona os elementos recebidos na tabela
tbody.append(row(user));
{);
{
{
Para adicionar linhas na tabela, utilizamos a função row()
, que retorna uma linha. Nesta linha, são definidas as referências para editar e remover um usuário.
Para obter um usuário específico, usamos a função getUser()
:
async function getUser(id) {
const response = await fetch("/api/users/" + id, {
method: "GET",
headers: { Accept: "application/json" },
});
if (response.ok === true) {
const user = await response.json();
const form = document.forms["userForm"];
form.elements["id"].value = user.id;
form.elements["name"].value = user.name;
form.elements["age"].value = user.age;
}
}
O usuário específico é adicionado ao formulário acima da tabela. Este mesmo formulário é utilizado tanto para adicionar quanto para editar um objeto. Com um campo oculto que armazena o id do usuário, podemos determinar qual ação está sendo executada: adicionar ou editar. Se o id for igual a 0, a função createUser
é executada, enviando os dados em uma requisição POST:
async function createUser(userName, userAge) {
const response = await fetch("api/users", {
method: "POST",
headers: { Accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({
name: userName,
age: parseInt(userAge, 10),
}),
});
if (response.ok === true) {
const user = await response.json();
reset();
tbody.append(row(user));
}
}
Se o usuário já foi carregado no formulário e seu id
está armazenado no campo oculto, a função editUser
é executada, enviando uma requisição PUT:
Ao executar a aplicação e acessar no navegador o endereço http://localhost:3000
, será possível adicionar, editar e remover usuários: