Vacinados Control REST API
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:
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:
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!