Vacinados Control REST API

Marcelo Lopes
11 min readAug 4, 2021

--

Vamos desenvolver esta API em Node.js, por isso o primeiro passo seria instalar o Node.js, se ja o tem instalado vamos passar a base de dados, vamos utilizar a MongoDB.

Inicialmente temos de criar conta se ainda não possui:

https://account.mongodb.com/account/register

Criamos um novo projeto no MongoDB

Criamos um Cluster

Temos também de criar um utilizador, adicionar um ip e criar uma base de dados:

Now we are going to use MongoDB Compass, if you don’t have it download here:

https://www.mongodb.com/try/download/compass

After you install MongoDB Compass you have to connect it with the cluster we created.

De seguida passamos ao desenvolvimento da API

Para inicializarmos o projeto usamos o seguinte código:

npm init -y

Depois de inicializarmos o projeto instalamos todos os módulos e bibliotecas que vamos precisar:

npm install bcrypt body-parser dotenv express jsonwebtoken mongoose swagger-jsdoc@6.1.0 swagger-ui-express

De seguida vamos criar um repositório no github.com

Fazer clone para uma pasta.

E adicionar a seguinte estrutura de pastas e ficheiros.

index.js

require("dotenv").config();const express = require("express");const mongoose = require("./database/index");let routes = require("./routes");const app = express();app.use(express.json());app.use(express.urlencoded({ extended: false }));const swaggerJSDoc = require('swagger-jsdoc');const swaggerUi = require('swagger-ui-express');const swaggerDefinition = {openapi: '3.0.0',info: {title: "API REST  Vacinados Portugal",version: "1.0.0",description: " API com informação de vacinados no ano 2021",license: {name: "Marcelo Lopes",}},};const options = {swaggerDefinition,apis: ['./swagger/*.js']};const swaggerSpec = swaggerJSDoc(options);app.get('/', (req, res) => {res.redirect('/api-docs');});app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));require('./routes/index')(app);const port = process.env.PORT || 8080;app.listen(port, () => {console.log(' Server is up and listening on port ' + port);});

utils/generateToken.js

// to use .env file variablesrequire("dotenv").config();// used to create, verify, and decode tokensconst jwt = require("jsonwebtoken");// contains secret key used to sign tokensconst secret = process.env.jwtSecret;// create a jwt tokenfunction generateJWTToken(userId) {// payload for jwtconst payload = {id: userId};return jwt.sign(payload, secret, { expiresIn: "10min" });}module.exports = generateJWTToken;

swagger/swagger.js

/*** @swagger* components:*   securitySchemes:*     bearerAuth:*       type: http*       scheme: bearer*       bearerFormat: JWT*   schemas:*     LoginUser:*       type: object*       properties:*         email:*           type: string*           description: The user email.*           example: express@gmail.com*         password:*           type: string*           description: The user password.*           example: express_password*     RegisterUser:*       type: object*       properties:*         email:*           type: string*           description: The user email.*           example: express@gmail.com*         password:*           type: string*           description: The user password.*           example: express_password*         name:*           type: string*           description: The user name.*           example: express_user*     User:*       type: object*       properties:*         _id:*           type: objectId*           description: The user ID.*           example: 60535c8a0f1f7e244eee6f79*         email:*           type: string*           description: The user email.*           example: express@gmail.com*         name:*           type: string*           description: The user name.*           example: express_user** /user/register:*   post:*     tags:*       - User Requests*     summary: Registe um utilizador.*     requestBody:*       required: true*       content:*         application/json:*           schema:*             $ref: '#/components/schemas/RegisterUser'*     responses:*       201:*         description: Criou um utilizador.*         content:*           application/json:*             schema:*               type: object*               properties:*                 auth:*                   type: boolean*                   description: O valor booleano da autenticação.*                   example: true*                 token:*                   type: string*                   description: O valor jsonwebtoken.*                   example: eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiVmFnbmVyQm9tSmVzdXMiLCJVc2VybmFtZSI6IlZhZ25lciBCb20gSmVzdXMifQ.v4BcDDTFqMXhpi7ofKmhDLkkiiNtPXYlvZGgS8gU38M** /user/login:*   post:*     tags:*       - User Requests*     summary: Login.*     requestBody:*       required: true*       content:*         application/json:*           schema:*             $ref: '#/components/schemas/LoginUser'*     responses:*       201:*         description: Logged in.*         content:*           application/json:*             schema:*               type: object*               properties:*                 auth:*                   type: boolean*                   description: The authentication boolean value.*                   example: true*                 token:*                   type: string*                   description: The jsonwebtoken value.*                   example: eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiVmFnbmVyQm9tSmVzdXMiLCJVc2VybmFtZSI6IlZhZ25lciBCb20gSmVzdXMifQ.v4BcDDTFqMXhpi7ofKmhDLkkiiNtPXYlvZGgS8gU38M** /user/isAuthorized:*   get:*     tags:*       - User Requests*     summary: Verifique a autorização do utilizador.*     security:*       - bearerAuth: []*     responses:*       201:*         description: Verifique a autorização do utilizador.*         content:*           application/json:*             schema:*               $ref: '#/components/schemas/User'*//*** @swagger* components:*   securitySchemes:*     bearerAuth:*       type: http*       scheme: bearer*       bearerFormat: JWT*   schemas:*     NovosVacinados:*       type: object*       properties:*         name:*           type: string*           description: Nome da pessoa.*           example: Marcelo*         distrito:*           type: string*           description: O distrito da pessoa.*           example: Viseu*         vacina:*           type: string*           description: O vacina que a pessoa levou ou vai levar.*           example: Pfizer*         profissao:*           type: string*           description: Profissão da pessoa.*           example: Engenheiro*         datavacina:*           type: string*           description: Data da vacina.*           example: 12/7/2021*         estadoatual:*           type: string*           description: Estado atual da pessoa.*           example: Vacinado**** /vacinado/create:*   post:*     tags:*       - Vacinados Requests*     summary: Introduza uma pessoa vacinada ou não.*     requestBody:*       required: true*       content:*         application/json:*           schema:*              $ref: '#/components/schemas/NovosVacinados'*     security:*       - bearerAuth: []*     responses:*       200:*         description: Introduza uma pessoa vacinada ou não.*         content:*           application/json:*             schema:*               type: string*               example: Pessoa criada.*** /vacinado/list:*   get:*     tags:*       - Vacinados Requests*     summary: Lista de pessoas vacinados e nao vacinadas.*     description: Retorna a lista de pessoas vacinados e nao vacinadas*     responses:*       200:*         description: OK.** /vacinado/search/datavacina:*   get:*     tags:*       - Vacinados Requests*     summary: Pesquisa por Data de vacinaçao.*     description: Os dados são gerados através dos dados obtidos pelo sistema*     parameters:*       - in: query*         required: false*         name: datavacina*         description: Especifique a data no formato dd/mm/yy.*         example: 18/5/2021*     responses:*       200:*         description: A lista das pessoas vacinados e nao vacinadas.*** /vacinado/search/distrito:*   get:*     tags:*       - Vacinados Requests*     summary: Pesquisa por Distrito.*     description: Os dados são gerados através dos dados obtidos pelo sistema*     parameters:*       - in: query*         required: false*         name: distrito*         description: Especifique um distrito.*         example: Viseu*         schema:*             enum: ["Viseu", "Lisboa", "Porto"]*     responses:*       200:*         description: A lista das pessoas no distrito.*** /vacinado/search/vacina:*   get:*     tags:*       - Vacinados Requests*     summary: Pesquisa por Vacina.*     description: Os dados são gerados através dos dados obtidos pelo sistema*     parameters:*       - in: query*         required: false*         name: vacina*         description: Especifique uma Vacina.*         example: Pfizer*         schema:*             enum: ["Moderna", "Pfizer", "AstraZeneca"]*     responses:*       200:*         description: A lista das pessoas com esta Vacina.*** /vacinado/search/name:*   get:*     tags:*       - Vacinados Requests*     summary: Pesquisa por Nome.*     description: Os dados são gerados através dos dados obtidos pelo sistema*     parameters:*       - in: query*         required: false*         name: name*         description: Especifique o Nome.*         example: Marcelo Lopes*     responses:*       200:*         description: A lista das pessoas com este nome.*** /vacinado/search/estadoatual:*   get:*     tags:*       - Vacinados Requests*     summary: Pesquisa por estados.*     description: Os dados são gerados através dos dados obtidos pelo sistema*     parameters:*       - in: query*         required: false*         name: estadoatual*         description: Especifique o estado atual.*         schema:*             enum: ["Vacinado", "Não Vacinado"]*     responses:*       200:*         description: A lista das pessoas neste estado.*** /vacinado/search/profissao:*   get:*     tags:*       - Vacinados Requests*     summary: Pesquisa por profissão.*     description: Os dados são gerados através dos dados obtidos pelo sistema*     parameters:*       - in: query*         required: false*         name: profissao*         description: Especifique a profissão.*         schema:*             enum: ["Jogador", "Astronauta", "Médico", "Engenheiro"]*     responses:*       200:*         description: A lista das pessoas com esta profissão.**/

routes/index.js

const user = require("./userRoutes");const vacinado = require("./vacinadoRoutes");// export the routesmodule.exports = (app) => {app.use("/user", user);app.use("/vacinado", vacinado);};

routes/userRoutes.js

const { Router } = require("express");const router = new Router();const bcrypt = require("bcrypt");const verifyToken = require("../middlewares/verifyToken");const generateToken = require("../utils/generateToken");const User = require("../models/userModels");// user/registerrouter.post("/register", async (req, res) => {try {await User.init();let user = await User.findOne({ email: req.body.email });if (user) return res.status(409).send('Um utilizador com este email já existe.');// hashed passwordconst saltRounds = 10;const salt = await bcrypt.genSalt(saltRounds);const hashedPassword = await bcrypt.hash(req.body.password, salt);// create useruser = await User.create({email: req.body.email,password: hashedPassword,name: req.body.name,});// generate jwt token with user.idlet token = generateToken(user.id);return res.status(200).send({ auth: true, token: token })} catch (err) {if (err.code == 11000 && err.keyPattern.email == 1)res.status(500).send('E-mail duplicado');elseres.status(500).send('Internal Server Error');}});// user/loginrouter.post('/login', async function (req, res) {try {// check if the user existsconst user = await User.findOne({ email: req.body.email });if (!user) return res.status(404).send('Invalid Credentials');// check if the password is correctconst validPassword = await bcrypt.compare(req.body.password, user.password);if (!validPassword) {return res.status(401).send({ auth: false, token: null, msg: 'Invalid Credentials' });}// generate jwt token with user.idlet token = generateToken(user.id);return res.status(200).send({ auth: true, token: token })} catch (err) {res.status(500).send('Internal Server Error');}});// user/isAuthorizedrouter.get('/isAuthorized', verifyToken, async function (req, res) {try {const user = await User.findById(req.userId, { password: 0 });if (!user) return res.status(404).send("Nenhum user encontrado.");res.status(200).send(user);} catch (err) {res.status(500).send("Ocorreu um problema ao localizar o user.");}});// export the router functionmodule.exports = router;

routes/vacinadoRoutes.js

// importing the express library but destructuring just the Router logicvar express = require('express');var router = express.Router();const Vacinado = require("../models/vacinadoModels");// vacinado/createrouter.post("/create", (req, res) => {try {var vacinado = new Vacinado(req.body);if (!vacinado) {return res.status(409).send('Verifique a ligação.');}vacinado.save();return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});// vacinado/listrouter.get("/list", async (req, res) => {try {// check if any vacinated person existsconst vacinado = await Vacinado.find();if (!vacinado) {return res.status(409).send('No vacinated person exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});router.get("/search/datavacina", async (req, res) => {try {const query = req.query;// check if any total existsconst vacinado = await Vacinado.find(query);if (!vacinado) {return res.status(409).send('No person was vacinated in this day exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});router.get("/search/distrito", async (req, res) => {try {const query = req.query;// check if any total existsconst vacinado = await Vacinado.find(query);if (!vacinado) {return res.status(409).send('No person in this district exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});router.get("/search/vacina", async (req, res) => {try {const query = req.query;// check if any total existsconst vacinado = await Vacinado.find(query);if (!vacinado) {return res.status(409).send('No person with this vaccine exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});router.get("/search/name", async (req, res) => {try {const query = req.query;// check if any total existsconst vacinado = await Vacinado.find(query);if (!vacinado) {return res.status(409).send('No person with this name exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});router.get("/search/estadoatual", async (req, res) => {try {const query = req.query;// check if any total existsconst vacinado = await Vacinado.find(query);if (!vacinado) {return res.status(409).send('No person vacinated or no vacinated exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});router.get("/search/profissao", async (req, res) => {try {const query = req.query;// check if any total existsconst vacinado = await Vacinado.find(query);if (!vacinado) {return res.status(409).send('No person with this profession exists.');}return res.status(200).send(vacinado);} catch (err) {res.status(500).send('Internal Server Error');}});module.exports = router;

models/userModels.js

const mongoose = require('mongoose');const Schema = mongoose.Schema;const UserSchema = new Schema({name: {type: String,required: true,},email: {type: String,required: true,unique: true,},password: {type: String,required: true,},});mongoose.model('User', UserSchema);module.exports = mongoose.model('User');

models/vacinadoModels.js

const mongoose = require('mongoose');const Schema = mongoose.Schema;const VacinadosSchema = new Schema({name: {type: String,required: true,},distrito: {type: String,required: true,},datavacina: {type: String,required: true,format: "DD/MM/YY"},estadoatual: {type: String,required: true,},profissao: {type: String,required: true,},vacina: {type: String,required: true,},});mongoose.model('Vacinado', VacinadosSchema);module.exports = mongoose.model('Vacinado');

middlewares/isAuthorized.js

// import model schemaconst User = require("../models/userModels");// check if user is authorized (exists in the database)const isAuthorized = async (req, res, next) => {try {await User.findById(req.userId, { password: 0 }, function (err, user) {if (err) return res.status(500).send("There was a problem.");if (!user) return res.status(404).send("You must have an account to make this request.");next();});} catch (err) {console.log('isAuthorized:\n' + err);res.status(500).send('Internal Server Error');}}module.exports = isAuthorized;

middlewares/verifyToken.js

// to use .env file variablesrequire("dotenv").config();// used to create, verify, and decode tokensconst jwt = require("jsonwebtoken");// contains secret key used to sign tokensconst secret = process.env.jwtSecret;// check if the token is validconst verifyToken = async (req, res, next) => {try {// get bearer token from headerconst authorizationHeader = req.header('authorization');// check if bearer token and token exist and assign only tokenconst token = authorizationHeader && authorizationHeader.split(' ')[1];//console.log(authorizationHeader);// check if the token exists and if not then return not authorizedif (!token)return res.status(403).send({ auth: false, message: 'No token provided.' });// check if the token is validconst payload = jwt.verify(token, secret);//console.log(payload + "teste")// append the payload id to the requestreq.userId = payload.id;next();} catch (err) {//console.log('VerifyToken:\n' + err);res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });}}module.exports = verifyToken;

database/index.js

// to use .env file variablesrequire("dotenv").config();// import mongoose libraryconst mongoose = require('mongoose');// connect to MongoDBmongoose.connect(`mongodb+srv://user_admin:useradmin123@cluster0.x6cul.mongodb.net?retryWrites=true&w=majority`,{useNewUrlParser: true,useUnifiedTopology: true,useFindAndModify: false,useCreateIndex: true}).then(() => console.log(' Connected to successfully!')).catch(err => console.log(err))module.exports = mongoose;

E assim concluímos a nossa API!

--

--

Marcelo Lopes
Marcelo Lopes

No responses yet