Introdução
Dando continuidade ao meu artigo anterior sobre Redis, hoje irei demonstrar como criar uma API utilizando Node.js, MongoDB e o Redis como cache. Caso tenha interesse em ler o artigo anterior, segue o link:
Para pular a etapa de criação de um novo projeto, eu irei utilizar um que eu desenvolvi em um artigo anterior. Caso tenha interesse em clonar esse projeto, segue o link no GitHub: node-express.
Estrutura do projeto
O projeto está bem simples, eu criei um controller contendo os 5 verbos para um CRUD e um diretório para criação das rotas. Abaixo, você tem uma imagem demonstrando a estrutura desse projeto:

Para os próximos passos, será necessário um editor de textos. Eu irei utilizar o VS Code, mas você pode escolher um de sua preferência.
O primeiro passo será baixar as bibliotecas necessárias para o utilizar o Redis e o Mongoose. Para isso, abra um terminal e navegue até o seu projeto. Em seguida, execute o comando abaixo:
npm i body-parser mongoose redis --save
O próximo passo será a criação de um Schema. Para isso, crie um arquivo chamado db.js no seguinte caminho ./src/config/db.js. Agora abra esse arquivo no seu editor de textos e atualize ele com o trecho de código abaixo:
var mongoose = require('mongoose'); mongoose.connect('mongodb://127.0.0.1/db_employees'); var Schema = mongoose.Schema; var personSchema = new Schema({ name: String, mail: String, role: String }); var Person = mongoose.model('Person', personSchema); module.exports = Person;
O arquivo acima irá criar uma collection. Nós iremos utilizar ela nos próximos passos desse artigo.
Agora, para deixar o código mais organizado, vamos criar um diretório no nosso projeto para os nossos repositórios. Para isso, crie um novo diretório no seguinte path: ./src/repositories/, e dentro dele um arquivo chamado personRepository.js.

Esse repositório será responsável pela conexão com o Schema. Atualize o arquivo personRepository.js com o código abaixo:
'use strict'; var mongoose = require('mongoose') var Person = require('../config/db'); module.exports = new class PersonRepository { getAll() { return Person.find(); } getById(id) { return Person.findById(id); } create(person) { return Person.create(person); } update(id, person) { const updatedperson = { name: person.name, mail: person.mail, role: person.role, } return Person.findByIdAndUpdate(id, updatedperson, { new: true }); } delete(id) { return Person.findByIdAndRemove(id); } }
Antes de atualizar o controller, vamos adicionar o bodyParser no projeto. Abra o seu arquivo app.js e atualize ele com o código abaixo.
const express = require('express'); const app = express(); const router = express.Router(); const bodyParser = require("body-parser"); //Rotas const index = require('./routes/index'); const personRoute = require('./routes/personRoute'); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use('/', index); app.use('/person', personRoute); module.exports = app;
Agora atualize o seu arquivo ./src/controllers/personController.js com o código a seguir:
'use strict'; const PersonRepository = require('../repositories/personRepository'); exports.get = (req, res, next) => { PersonRepository.getAll() .then((person) => { res.status(200).send(person); }).catch(err => res.status(500).send(err)) }; exports.getById = (req, res, next) => { PersonRepository.getById(req.params.id) .then((person) => { res.status(200).send(person); }).catch(err => res.status(500).send(err)) }; exports.post = (req, res, next) => { const p = req.body; PersonRepository.create(p) .then((person) => { res.status(200).send(person); }).catch(err => res.status(500).send(err)) }; exports.put = (req, res, next) => { const p = req.body; PersonRepository.update(req.params.id, p) .then((person) => { res.status(201).send(person); }).catch(err => res.status(500).send(err)) }; exports.delete = (req, res, next) => { PersonRepository.delete(req.params.id) .then((person) => { res.status(200).send('delete succeeded!'); }).catch(err => console.error.bind(console, `Error ${err}`)) };
Navegando rapidamente pelo código acima você tem:
- 03: chamada para o repositório PersonRepository
- 05 a 43: implementação dos verbos com a chamada ao nosso PersonRepository
Testando as rotas
Para não precisar instalar o MongoDB no meu computador, eu irei utilizar ele dentro de um contêiner Docker. Caso tenha interesse em utilizar o Mongo com Docker, segue link de um artigo onde eu demonstro esse passo a passo:
Agora, para testar as nossas rotas eu irei utilizar o postman. Caso não conheça ele ainda, o postman nos permite enviar requisições HTTP e obter suas respostas.
POST
A primeira rota que nós precisamos testar é o post, ela que irá cadastrar os nossos registros. Com o postman aberto, preencha ele conforme a imagem abaixo:

GET
Agora altere o verbo para GET e veja o resultado abaixo:

GETById
Buscando uma pessoa pelo seu Id:

PUT
Para validar o PUT, eu irei atualizar o campo role para System Architect.
DELETE
Agora vamos deletar o nosso registo:
Bom, com isso nós conseguimos verificar que todos os nossos verbos estão funcionando corretamente. Para que possamos testar o Redis, será necessário ter alguns registros cadastrados. Para isso, eu irei cadastrar o Zé Maria 10x.
Configurando o Redis
Para não precisar instalar o Redis no meu computador, eu irei utilizar ele da mesma forma que estou utilizando o Mongo, dentro de um contêiner Docker. Caso tenha interesse em saber como criar um contêiner com Redis, segue link de um artigo onde eu demonstro esse passo:
O próximo passo será adicionar o Redis no nosso controller. Para isso, atualize o arquivo personController.js com o trecho de código abaixo:
'use strict'; const PersonRepository = require('../repositories/personRepository'); var redis = require('redis'); var client = redis.createClient(); exports.get = (req, res, next) => { client.get('allpersons', function (err, reply) { if (reply) { console.log('redis'); res.send(reply) } else { console.log('db'); PersonRepository.getAll() .then((person) => { client.set('allpersons', JSON.stringify(person)); client.expire('allpersons', 20); res.status(200).send(person); }).catch(err => res.status(500).send(err)) } }); }; exports.getById = (req, res, next) => { PersonRepository.getById(req.params.id) .then((person) => { res.status(200).send(person); }).catch(err => res.status(500).send(err)) }; exports.post = (req, res, next) => { const p = req.body; PersonRepository.create(p) .then((person) => { res.status(200).send(person); }).catch(err => res.status(500).send(err)) }; exports.put = (req, res, next) => { const p = req.body; PersonRepository.update(req.params.id, p) .then((person) => { res.status(201).send(person); }).catch(err => res.status(500).send(err)) }; exports.delete = (req, res, next) => { PersonRepository.delete(req.params.id) .then((person) => { res.status(200).send('delete succeeded!'); }).catch(err => console.error.bind(console, `Error ${err}`)) };
O passo anterior foi bem simples, eu adicionei o Redis na linha 05, e em seguida eu criei um cliente na linha 06, depois eu criei um chave chamada allpersons que deve expirar em 20 segundos. Para verificar se a aplicação está batendo no banco ou no Redis, eu criei dois consoles retornando: Redis quando estiver no cache e um outro db para quando a API requisitar os dados do mongo.
Testando o Cache
Agora. para verificar se o Redis está configurado corretamente, execute o código novamente e note no seu terminal que ele bate uma vez no banco e depois fica no Redis por 20 segundos. Abaixo você tem uma imagem demonstrando esse passo:

Com isso nós finalizamos esse artigo. O meu intuito aqui foi demonstrar uma das formas de melhorar a performance dos nossos sistemas. Caso tenha interesse em baixar o código fonte desenvolvido nesse artigo, segue o link no GitHub:
Espero ter ajudado e até um próximo artigo, pessoal!