Apresentação
Olá, como vai? Eu sou o Vitor e estou de volta com um novo projeto. Já se passou algum tempo desde o meu último tutorial, pois estive ocupado com outras atividades nos últimos meses. No entanto, é hora de retornarmos ao fascinante mundo do código.
Hoje, vamos criar um servidor em Node.js para uma página de blog. Nosso servidor terá a capacidade de registrar usuários e autenticá-los usando JWT. Além disso, abordaremos temas como registro, consultas, edição e remoção de dados, como texto e imagens, do banco de dados MySQL.
Com a sua API pronta, você poderá alimentar a sua aplicação frontend. Caso ainda não tenha um site, siga para o tutorial de frontend*.
Ao longo deste tutorial, vou me esforçar para ser o mais sucinto e claro possível.
Você pode acessar o código fonte em:
Gondrak08 / blog-nodejs-server
A blog server made with Node.js and Mysql. This is part of a tutorial project.
Servidor para Blog - Node.js & Mysql.
Ingredientes
- bcryptjs - uma biblioteca de criptografia para senhas.
-
cors - uma middleware que nos permite lidar com
Cross-Origin Resource Sharing (CORS)
. - dotenv - um módulo que nos possibilita acessar variáveis em arquivos '.env'.
- express - uma biblioteca que nos permite criar APIs RESTful.
- jsonwebtoken - uma biblioteca para a criação dos nossos tokens de autenticação.
- multer - o Multer é uma biblioteca que nos permitirá salvar imagens em nosso servidor.
- mysql - uma biblioteca de conexão com o banco de dados MySQL.
Como usar
Instalando aplicação.
npm i
Conectando ao Banco de dados
Lembre-se de configurar a sua conexão ao banco de dados Mysql
Em connection.js
const mysql = require("mysql");
const dbConfig = {
host: "localhost",
user: "your db user",
password: "your db password",
database: "your
…Espero que você se divirta.
Bom código.
Bibliotecas
Aqui estão as bibliotecas que utilizaremos em nossa aplicação.
- bcryptjs - uma biblioteca de criptografia para senhas.
-
cors - uma middleware que nos permite lidar com
Cross-Origin Resource Sharing (CORS)
. - dotenv - um módulo que nos possibilita acessar variáveis em arquivos '.env'.
- express - uma biblioteca que nos permite criar APIs RESTful.
- jsonwebtoken - uma biblioteca para a criação dos nossos tokens de autenticação.
- multer - o Multer é uma biblioteca que nos permitirá salvar imagens em nosso servidor.
- mysql - uma biblioteca de conexão com o banco de dados MySQL.
Arquitetura
A estrutura de pastas e arquivos de nossa aplicação.
server
|
|- middleware/
| |- auth.js
| |- upload.js
|
|- routes/
| |- user.js
| |- article.js
|
|- uploads/
|- utils/
| |- trucantText.js
|
|- connection.js
|- server.js
|- tables.sql
Banco de dados
Antes de prosseguirmos com nossa aplicação, é necessário criar o banco de dados. Utilizaremos o banco de dados MySQL para desenvolver nosso projeto. Você pode optar pela plataforma de gerenciamento de banco de dados com a qual estiver mais familiarizado, como DBeaver, entre outras.
No meu caso, vou utilizar o terminal:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 8.0.35-0ubuntu0.22.04.1 (Ubuntu)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
Uma vez dentro de sua aplicação, crie o banco de dados:
mysql> create database blog_db;
Declaração de Tabelas
A seguir, escreveremos as tabelas que serão utilizadas em nosso banco de dados.
Caso esteja usando o terminal, acesse seu banco de dados:
mysql> use blog_db;
Uma vez dentro do seu banco de dados, copie cada tabela abaixo e cole em seu terminal.
- 1 Usuário
create table user(
id int primary key AUTO_INCREMENT,
name varchar(250),
password varchar(250),
email varchar(50),
role varchar(50),
UNIQUE(email)
);
- 2 Artigos
create table articles(
id int NOT NULL AUTO_INCREMENT,
userId int NOT NULL,
title varchar(250) NOT NULL,
image varchar(250) DEFAULT NULL,
content varchar(250) NOT NULL,
primary key(id)
);
- 3 RefreshToken
create table refreshToken(
id int primary key AUTO_INCREMENT,
expiresIn int,
userName varchar(250),
userId int NOT NULL
);
O resultado final de nossas tabelas será:
mysql> show tables;
+-------------------------+
| Tables_in_blog_db |
+-------------------------+
| articles |
| refreshToken |
| user |
+-------------------------+
3 rows in set (0.00 sec)
Por fim, vamos criar um usuário teste com o seguinte comando dentro do banco de dados:
mysql> insert into user(name, password, email, role) values ('admin','admin','admin@admin.com','admin');
Primeiros passos
Iniciando Aplicação
Vamos começar criando o package.json
. No terminal de sua escolha:
npm init
Faça a configuração de seu npm init
como entender.
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (test)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
Instalando dependências
Em seguida vamos instalar as bibliotecas.
npm i express multer mysql bcryptjs cors dotenv jsonwebtoken
Criando o servidor
Dentro da raiz do projeto, crie o arquivo server.js
e adicione o seguinte código:
const express = require("express");
const cors = require("cors");
// connection to the server
const dbConnection = require("./connection");
const router = require("./routes/user");
// import routs
const user = require("./routes/user");
const article = require("./routes/article");
const app = express();
// middleware
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use("/uploads", express.static("uploads"));
app.use(express.json());
const upload = require("./middleware/upload");
// declare routs
app.use("/user", user);
app.use("/articles", article);
// calling server
const PORT = 8080;
app.listen(PORT, () => {
{
console.log(`server is running at port ${PORT} `);
}
});
Pode ser que, neste momento, alguns dos arquivos ainda não tenham sido escritos, mas não se preocupe. Mais adiante, iremos criá-los.
Uma vez em que todos os módulos necessários forem instalados e a conexão com o banco de dados configurada corretamente antes de iniciar o servidor
-
caso queira fazer um teste, comente do código todas as rotas e conexão com banco dedados, deixando apenas o
app.listen
Podemos escrever no terminal:
$ node server.js
E nosso servidor irá rodar na porta 8080.
Conexão ao Banco de Dados
Fazendo uso da biblioteca mysql
, vamos escrever o código que conectará o nosso servidor ao banco de dados.
Lembre-se de substituir os valores pelos seus exatos para conectar ao banco de dados. Você pode registrá-los em seu arquivo .env
para uma melhor proteção.
Crie um arquivo chamado connection.js
com o seguinte conteúdo:
const mysql = require("mysql");
const dbConfig = {
host: "localhost",
user: "your db user",
password: "your db password",
database: "your db",
};
const dbConnection = mysql.createConnection(dbConfig);
module.exports = dbConnection;
Middleware
Dentro da pasta middleware
, iremos escrever dois arquivos.
Autenticação
Dentro do arquivo middleware/auth.js
, escreveremos o sistema responsável por validar a nossa chave de autenticação. Essa função será utilizada para proteger as rotas privadas de nossa aplicação, evitando que os dados de nosso banco de dados sejam acessados por usuários indevidos.
Primeiramente, dentro de nossa pasta raiz, vamos declarar um arquivo .env
. Em seguida, dentro desse arquivo, vamos salvar a nossa chave de autenticação.
TOKEN_KEY = "ColoqueAquiOSeuToken"
TOKEN_REFRESH = "InsiraAquiSeuTokenDeVaLiDaçÃo"
TOKEN_LIFE="300" // tempo de vida do token
TOKEN_LIFE_REFRESH="400" // tempo de vida do token refresh
Você pode utilizar o próprio terminal para gerar o seu token
. Primeiro, entre no node
$ node
Em seguida, utilizando da biblioteca crypto
:
> require("crypto").randomBytes(64).toString('hex')
O resultado será uma linha composta de números e letras.
Caso queira saber mais sobre jwt
, acesse sua documentação.
Agora vamos escrever nossa função verifyToken
dentro do arquivo auth.js
:
const jwt = require("jsonwebtoken");
const config = process.env;
const verifyToken = (req, res, next) => {
const token =
req.body.token || req.query.token || req.headers["x-access-token"];
if (!token)
return res.status(403).send("a token is required for authentication");
try {
const decoded = jwt.verify(token, config.TOKEN_KEY);
req.user = decoded;
} catch (err) {
return res.status(401).send({ message: "ivalid token" });
}
return next();
};
module.exports = verifyToken;
Upload de arquivos
Nosso blog fará uso de imagens na publicação de nossos artigos, para isso, vamos criar um middleware capaz de lidar com o path
das imagens e redirecioná-los para uma pasta específica de nossa aplicação: uploads/
.
- Faremos uso da biblioteca multer, para saber mais, acesse a sua documentação.
Crie um arquivo chamado uploads.js
dentro da pasta middleware
:
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads/");
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
},
});
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 10,
},
});
module.exports = upload;
Utilitários
Dentro de utils/
, iremos escrever a função truncanteText
. Seu uso é simples. A função recebe um texto e retorna dele uma parte pequena. Ela será usada para enviar um pequeno resumo do conteúdo do artigo como consulta de nossa API.
Crie um arquivo chamado truncanteText.js
dentro da pasta utils
com o seguinte conteúdo:
const trucanteText = (content, limit) => {
const filteredContent = content.replace(/#{1,3}\s|```
{% endraw %}
/g, "");
const words = filteredContent.split(" ");
if (words.length > limit) {
return words.slice(0, limit).join(" ") + "...";
}
return content;
};
module.exports = trucanteText;
{% raw %}
Rotas
Usuário(User)
Para escrever nossas rotas de usuário, utilizaremos as bibliotecas:
-
express
- para a criação de nossas rotas. -
jsonwebtoken
- para a criação de nossa autenticação e sua chave de assinatura. -
connection.js
- o nosso módulo de conexão para realizar as nossas queries ao banco de dados.
Em routes/user.js
:
js
require("dotenv").config();
const express = require("express");
const dbConnection = require("../connection");
const jwt = require("jsonwebtoken");
const router = express.Router();
Em seguida, escreveremos três rotas de usuário:
1 - /Signup - rota para o método de registro de novos usuários em nosso banco de dados.
- Defina uma rota HTTP POST para
/signup
.
javascript
router.post("/signup", (req, res) => {
...
}
- Obtemos os dados do novo usuário,"nome", "email" e "password", através do seu request.
javascript
const user = req.body
- Escrita da nossa consulta sql,
const query = "select email, name, password from user where email=?";
- Consulta ao banco de dados, resultado final do código:
javascript
router.post("/signup", (req, res) => {
const user = req.body;
const query = "select email, name, password from user where email=?";
dbConnection.query(query, [user.email], (err, result) => {
if (!err) {
if (result.length <= 0) {
const query = "insert into user (name,email,password) values(?,?,?)";
dbConnection.query(
query,
[user.name, user.email, user.password],
(err, results) => {
if (!err) {
return res
.status(200)
.json({ message: "You are Successfully Registrated" });
} else {
return res.status(500).json(err);
}
},
);
} else {
return res.status(400).json({ message: "Email already registrated!" });
}
}
return res.status(500).json(err);
});
});
2. /Login - vamos declarar a rota para a autenticação dos usuários em nossa aplicação.
- Defina uma rota HTTP POST para
/Login
.
javascript
router.post("/login", (req, res) => {
...
}
- Obtemos os dados do usuário, "email" e "password", através do seu request.
javascript
const user = req.body
- Escrita de nossa consulta sql,
javascript
const query = "select id, name, email, password from user where email=?";
- Consulta ao banco de dados, tratamento do resultado da consulta, e geração de token:
javascript
router.post("/login", (req, res) => {
const user = req.body;
const query = "select id, name, email, password from user where email=?";
dbConnection.query(query, [user.email], (err, result) => {
if (!err) {
if (result.length <= 0 || result[0].password != user.password) {
return res
.status(401)
.json({ message: "wrong password, try it again" });
} else if (result[0].password === user.password) {
const response = {
userId: result[0].id,
name: result[0].name,
email: result[0].email,
};
const accessToken = jwt.sign(response, process.env.TOKEN_KEY, {
expiresIn: process.env.TOKEN_LIFE,
});
const refreshToken = jwt.sign(response, process.env.TOKEN_REFRESH, {
expiresIn: process.env.TOKEN_LIFE_REFRESH,
});
res.status(200).json({
token: accessToken,
expiresIn: process.env.TOKEN_LIFE,
tokenRefresh: refreshToken,
name: result[0].name,
email: result[0].email,
id: result[0].id,
});
}
} else {
return res.status(500).json({ message: "something went wrong", err });
}
});
});
Se as credenciais estiverem corretas, gera dois tokens JWT: um para o acesso regular (accessToken) e outro para refresh
(refreshToken). Os tokens contêm informações sobre o usuário, como id, nome e email. Os tokens são assinados usando chaves secretas definidas nas variáveis de ambiente.
3. /Refresh-Token - Rota para a ré-autenticação de nosso usuário na aplicação.
- Defina uma rota HTTP POST para "/refresh-token".
javascript
router.post("/refresh-token", (req, res) => {...}
- Obtemos o token de atualização prévio do usuário através do
request
:
javascript
const refreshToken = req.body.refreshToken;
- Verificamos o nosso token de atualização
javascript
if (!refreshToken)
return res.status(401).json({ message: "Refresh token missing" });
- Renovamos os token de acesso:
javascript
const token = jwt.sign(response, process.env.TOKEN_KEY, {
expiresIn: process.env.TOKEN_LIFE,
});
const refreshToken = jwt.sign(response, process.env.TOKEN_REFRESH, {
expiresIn: process.env.TOKEN_LIFE_REFRESH,
});
- Aqui está o resultado final:
javascript
router.post("/refresh-token", (req, res) => {
const refreshToken = req.body.refreshToken;
if (!refreshToken)
return res.status(401).json({ message: "Refresh token missing" });
jwt.verify(refreshToken, process.env.TOKEN_REFRESH, (err, decoded) => {
if (!err) {
const response = {
userId: decoded.id,
name: decoded.name,
email: decoded.email,
};
const token = jwt.sign(response, process.env.TOKEN_KEY, {
expiresIn: process.env.TOKEN_LIFE,
});
const refreshToken = jwt.sign(response, process.env.TOKEN_REFRESH, {
expiresIn: process.env.TOKEN_LIFE_REFRESH,
});
res.status(200).json({
id: decoded.id,
name: decoded.name,
email: decoded.email,
token: token,
expiresIn: process.env.TOKEN_LIFE,
tokenRefresh: refreshToken,
});
} else {
res.status(500).json(err);
}
});
})
Artigos(Articles)
Em routes/article.js
, vamos começar chamando as bibliotecas:
-
express
- para a criação de nossas rotas. -
jsonwebtoken
- para a criação de nossa autenticação e sua chave de assinatura. -
connection.js
- o nosso módulo de conexão para realizar as nossas queries ao banco de dados. -
authentication.js
- nosso middleware de autenticação para proteção das rotas privadas. -
upload.js
- nosso middleware para o upload de imagens.
javascript
const express = require("express");
const dbConnection = require("../connection");
const fs = require("fs");
const path = require("path");
//middleware
const upload = require("../middleware/upload");
const authentication = require("../middleware/auth");
// utils
const trucanteText = require("../utils/trucanteText");
const router = express.Router();
//routes...
module.exports = router;
Em seguida vamos escrever as rotas:
1./add
- Rota para adicionar novo artigo.
- Definimos uma rota HTTP POST para "/add", passando o middleware
authentication
e oupload.single("thumb")
, para o upload de uma única imagem:
javascript
router.post("/add", upload.single("thumb"), authentication, (req, res, next) => {
...
}
- Coletamos os dados do corpo da solicitação
(req.body)
relacionados ao novo artigo, comouserId
,title
, econtent
. Além disso, obtém o caminho da imagem carregada usando o middleware multer e o nome do campo especificado ("thumb").
...
const { userId, title, content } = req.body;
const image = req.file.path;
- Escrita da consulta sql para inserir os dados do novo artigo dentro da tablea
articles
do banco de dados. Os valores inseridos são obtidos das variáveis userId, title, image, e content.
javascript
const query ="insert into articles (userId, title, image, content) values(?,?,?,?)";
- O resultado final do nosso código:
javascript
router.post(
"/add",
upload.single("thumb"),
authentication,
(req, res, next) => {
const { userId, title, content } = req.body;
const image = req.file.path;
const query =
"insert into articles (userId, title, image, content) values(?,?,?,?)";
dbConnection.query(
query,
[userId, title, image, content],
(err, result) => {
if (!err) {
return res.status(200).json({ message: "article has been added" });
} else {
return res.status(500).json(err);
}
},
);
},
);
2. /delete/:id
- Para a remoção de artigos.
- Defina uma rota HTTP DELETE para "/delete/:id". Esta rota será usada para remover um artigo do banco de dados. A autenticação é verificada antes de permitir a execução desta rota.
javascript
router.delete("/delete/:id", authentication, (req, res) => {
...
}
- Obtém o parâmetro id da URL usando req.params. Este id é usado para identificar o artigo que será removido do banco de dados.
javascript
const id = req.params.id;
- Escreva uma consulta sql para excluir um artigo da tabela "articles" do banco de dados com base no
id
fornecido.
javascript
const query = "delete from articles where id=?";
- Aqui está o resultado final do nosso código:
javascript
router.delete("/delete/:id", authentication, (req, res) => {
const id = req.params.id;
const query = "delete from articles where id=?";
dbConnection.query(query, [id], (err, result) => {
if (!err) {
if (result.affectedRows == 0) {
return res.status(404).json({ message: "article id does not found" });
} else {
return res.status(200).json({ message: "article deleted" });
}
} else {
return res.status(500).json(err);
}
});
});
3. /edit/:id
- Rota para a edição de Artigos.
- Defina uma rota HTTP PATCH para "/edit/:id". Esta rota será usada para atualizar um artigo no banco de dados. A autenticação é verificada antes de permitir a execução desta rota.
javascript
router.patch("/edit/:id", authentication, (req, res) => {...}
- Coletamos o parâmetro
id
da URL e os dados do artigo do corpo da solicitação(req.body).
O id é usado para identificar o artigo que será atualizado.
javascript
const id = req.params.id;
const article = req.body;
- Escrita da consulta sql para atualizar um artigo na tabela "articles" do banco de dados com base no id fornecido.
javascript
const query = "update articles set title=?, content=? where id=?";
- Aqui está o resultado final de nosso código:
javascript
router.patch("/edit/:id", authentication, (req, res) => {
const id = req.params.id;
const article = req.body;
const query = "update articles set title=?, content=? where id=?";
dbConnection.query(
query,
[article.title, article.content, id],
(err, result) => {
if (!err) {
if (result.affectedRows == 0) {
return res.status(404).json({ message: "article id does not found" });
} else {
return res
.status(200)
.json({ message: "article updated succesfullt" });
}
} else {
return res.status(500).send({ message: "something went wrong" }, err);
}
},
);
});
4. /getAll
- Rota de consulta para todos os Artigos
- Defina uma rota HTTP GET para "/getAll". Essa rota será usada para obter todos os artigos registrados no banco de dados.
javascript
router.get("/getAll", (req, res) => {}
- Escreva uma consulta sql para selecionar todos os campos de todos os artigos na tabela "articles" do banco de dados.
javascript
const query = "SELECT * FROM articles";
- Tratamento da consulta e resultado final:
javascript
router.get("/getAll", (req, res) => {
const query = "SELECT * FROM articles";
dbConnection.query(query, (err, results) => {
if (err) {
res.status(500).send({ message: "could not get articles" });
} else {
const processedResults = results.map((article) => {
const { content, ...rest } = article;
const resumedContent = trucanteText(content, 5);
return { content: resumedContent, ...rest };
});
res.json(processedResults);
}
});
});
5. /getById/:id
- Rota para obter um Artigo com base no ID.
- Defina uma rota HTTP GET para "/getById/:id".
js
router.get("/getById/:id", (req, res) => {...}
- Coletamos o parâmetro
id
da URL usandoreq.params
.
js
const id = req.params.id;v
- Escreva uma consulta sql para selecionar todos os campos do artigo na tabela "articles" do banco de dados onde o id corresponde ao id fornecido na URL.
javascript
const query = "SELECT * FROM articles WHERE id=?";
- Aqui temos o resultado final do nosso código:
javascript
router.get("/getById/:id", (req, res) => {
const id = req.params.id;
const query = "SELECT * FROM articles WHERE id=?";
dbConnection.query(query, [id], (err, results) => {
if (err) {
res.status(500).send({ message: "something whent wrong" }, err);
} else if (results.length === 0) {
res.status(404).send({ message: "article not found" });
} else {
res.json(results[0]);
}
});
});
Com todas essas rotas escritas vamos se capazes de consultar, registrar, editar e remover os dados de nossa aplicação.
Conclusão
Primeiramente, gostaria de agradecer por dedicar seu tempo a este tutorial, e parabéns por concluí-lo! Espero que tenha sido útil e fácil de seguir.
Sinta-se à vontade para explorar mais suas habilidades nesta aplicação. Algumas rotas foram deixadas de fora propositalmente para que você se sinta tentado a escrevê-las por si mesmo. Caso não tenha nada em mente, aqui estão algumas sugestões:
- Rota para edição dos dados de um usuário
- Rota para recuperação de senha de usuário via e-mail.
- Rota para atualização de senha de usuário.
- Rota para download de um artigo específico em formato markdown.
Este tutorial também abrange a parte frontend
de nossa aplicação. Afinal, para que você quer uma API se não for usá-la em algum lugar, não é mesmo? Portanto, avance para a próxima etapa, acesse aqui e continue o nosso tutorial.
Além disso, não deixe de me seguir nas redes sociais.
Até a próxima! o/
Top comments (0)