Express e MongoDB
Em uma única aplicação, vamos unir o processamento de solicitações com o Express e o trabalho com dados no MongoDB. Para isso, definiremos o seguinte arquivo de aplicação app.js
:
const express = require("express");
const MongoClient = require("mongodb").MongoClient;
const objectId = require("mongodb").ObjectId;
const app = express();
app.use(express.static("public")); // arquivos estáticos estarão na pasta public
app.use(express.json()); // ativamos o parsing automático de JSON
const mongoClient = new MongoClient("mongodb://127.0.0.1:27017/");
(async () => {
try {
await mongoClient.connect();
app.locals.collection = mongoClient.db("usersdb").collection("users");
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
} catch (err) {
console.log(err);
}
})();
app.get("/api/users", async (req, res) => {
const collection = req.app.locals.collection;
try {
const users = await collection.find({}).toArray();
res.send(users);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
app.get("/api/users/:id", async (req, res) => {
const collection = req.app.locals.collection;
try {
const id = new objectId(req.params.id);
const user = await collection.findOne({ _id: id });
if (user) res.send(user);
else res.sendStatus(404);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
app.post("/api/users", async (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 };
const collection = req.app.locals.collection;
try {
await collection.insertOne(user);
res.send(user);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
app.delete("/api/users/:id", async (req, res) => {
const collection = req.app.locals.collection;
try {
const id = new objectId(req.params.id);
const user = await collection.findOneAndDelete({ _id: id });
if (user) res.send(user);
else res.sendStatus(404);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
app.put("/api/users", async (req, res) => {
if (!req.body) return res.sendStatus(400);
const userName = req.body.name;
const userAge = req.body.age;
const collection = req.app.locals.collection;
try {
const id = new objectId(req.body.id);
const user = await collection.findOneAndUpdate({ _id: id }, { $set: { age: userAge, name: userName } }, { returnDocument: "after" });
if (user) res.send(user);
else res.sendStatus(404);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
// escutando a interrupção do programa (ctrl-c)
process.on("SIGINT", async () => {
await mongoClient.close();
console.log("Aplicação encerrada");
process.exit();
});
Para cada tipo de solicitação, há um manipulador específico do Express. Em cada manipulador, acessamos o banco de dados. Para evitar abrir e fechar a conexão a cada solicitação, abrimos a conexão no início com uma função IIFE ( Imediately Invoked Function Expression) e só depois de abrir a conexão, iniciamos a escuta de solicitações:
(async () => {
try {
await mongoClient.connect();
app.locals.collection = mongoClient.db("usersdb").collection("users");
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
} catch (err) {
console.log(err);
}
})();
Como toda a interação será com a coleção users, obtemos uma referência a esta coleção na variável local da aplicação app.locals.collection
. Depois, através dessa variável, podemos acessar a coleção em qualquer parte da aplicação.
No final do script, podemos fechar a conexão armazenada na variável mongoClient
:
process.on("SIGINT", async () => {
await mongoClient.close();
console.log("Aplicação encerrada");
process.exit();
});
Neste caso, ouvimos o evento "SIGINT", que é disparado ao pressionar a combinação CTRL+C no console, encerrando a execução do script.
Quando uma solicitação GET é enviada à aplicação, retornamos todos os documentos da coleção users
:
app.get("/api/users", async (req, res) => {
const collection = req.app.locals.collection;
try {
const users = await collection.find({}).toArray();
res.send(users);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
Se um parâmetro id
for passado na solicitação GET, retornamos apenas um usuário do banco de dados com este id
:
app.get("/api/users/:id", async (req, res) => {
const collection = req.app.locals.collection;
try {
const id = new objectId(req.params.id);
const user = await collection.findOne({ _id: id });
if (user) res.send(user);
else res.sendStatus(404);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
Quando uma solicitação POST é recebida, usamos o parser JSON para obter os dados enviados e, com eles, criamos um objeto que adicionamos ao banco de dados através do método insertOne()
:
app.post("/api/users", async (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 };
const collection = req.app.locals.collection;
try {
await collection.insertOne(user);
res.send(user);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
});
Quando recebemos uma solicitação PUT, também obtemos os dados enviados e usamos o método findOneAndUpdate()
para atualizar os dados no banco de dados.
No método app.delete()
, acionado ao receber uma solicitação DELETE, chamamos o método findOneAndDelete()
para excluir os dados.
Assim, em cada manipulador do Express, usamos um método específico para trabalhar com o MongoDB.
Definindo o Código em index.html
Agora, criaremos uma nova pasta chamada public
no diretório do projeto e definiremos nela o arquivo index.html
.
mongoapp/ │ ├── node_modules/ │ ├── public/ │ └── index.html │ ├── app.js ├── package.json └── package-lock.json
No arquivo index.html
, definiremos o seguinte código:
<!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");
// Obtendo 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));
});
}
}
// Obtendo 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;
}
}
// Adicionando um 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));
}
}
// Editando um 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));
}
}
// Excluindo um 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();
}
}
// Resetando o formulário
function reset() {
const form = document.forms["userForm"];
form.reset();
form.elements["id"].value = 0;
}
// Criando uma 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("Excluir");
removeLink.addEventListener("click", (e) => {
e.preventDefault();
deleteUser(user._id);
});
linksTd.append(removeLink);
tr.appendChild(linksTd);
return tr;
}
// Resetando os valores do formulário
document.getElementById("resetBtn").addEventListener("click", (e) => {
e.preventDefault();
reset();
});
// Enviando o 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);
});
// Carregando os usuários
getUsers();
</script>
</body>
</html>
O código deste arquivo já foi explicado no tema sobre API em Node.js, e aqui ele é praticamente o mesmo.
Neste arquivo, definimos um formulário e uma tabela para gerenciar os usuários armazenados no banco de dados MongoDB. O código JavaScript no arquivo permite buscar, adicionar, editar e excluir usuários através de chamadas à API do Node.js que definimos anteriormente.
Como o Express usa a pasta public
para armazenar arquivos estáticos, ao acessar a aplicação pela rota raiz http://localhost:3000
, o cliente receberá este arquivo.
Vamos iniciar a aplicação, acessar http://localhost:3000
e poderemos gerenciar os usuários armazenados no banco de dados MongoDB: