Motor de API REST/GraphQL em Node.js com MongoDB e Redis.
O objetivo desse projeto é facilitar e acelerar o desenvolvimento de APIs REST ou GraphQL de pequeno e medio porte. Onde funcionalidades comuns que desprendem de tempo são abreviados, para aumentar o foco na atividade fim e menos na atividade meio (tecnico).
Esse projeto tambem é uma forma de apresentar meu conhecimento. Sendo assim, estarei sempre aplicando novas abordagens, atualizando meus conhecimentos nele e aplicando novas tecnologias (utilizando a plataforma Node.js que gosto tanto). Sempre vou manter exemplos funcionais, para que possam usufruir e testar.
Para iniciar a utilização desse motor de APIs, é necessario 2 coisas:
No diretorio src/main/api existem 2 pastas: rest e graphql (as APIs estão nelas respectivamente).
Nas pastas das APIs contem um arquivo chamado route.yaml
, onde esta parametrizado informações de cadastro da API.
Temos as seguintes opções:
# API REST
# Para API Rest é necessario cadastrar o tipo, o metodo e a rota
# Tipo de API
type: 'rest'
# Metodo HTTP da API
method: 'get'
# Rota da API
uri: '/v1/funcionarios'
---
# API GraphQL
# Para API GraphQL é necessario cadastrar o tipo e o nome do arquivo que contem o schema
# Tipo de API
type: 'graphql'
# GraphQL schema
schema: 'funcionario.graphql'
No diretorio de cada API, contem a pasta de test, onde contem os testes utilizando os frameworks mocha para validação e o nyc para mostrar a cobertura teste numa forma visual.
Praticamente todos os modulos utilizam o objeto "Context", ele contem as seguintes funções:
## Objeto de apoio
Context:
## "get" é o nó principal
get:
## nó que informa que o modulo é local.
self:
## nó que informa que o modulo local precisa do Context.
context:
## funcao onde é passado a pasta e o nome do modulo e executa a funcao primaria passando o Context. Ex service/funcionario
module:
## funcao onde é passado a pasta e o nome do modulo
module:
## funcao onde é passado o nome da variavel que quer obter do servidor
server:
## funcao igual o comando "require" nativo
module:
Exemplo de camadas:
//// conector do mongo
const mongo = context.get.server('mongo');
//// conector do redis
const redis = context.get.server('redis');
//// logger
const logger = context.get.server('logger');
//// obtendo framework do node_modules
const _ = context.get.module('lodash');
//// chamando um modulo simples
// /service/funcionario.js
module.exports.consultar = function(id) {
return 123;
}
// /controller.js
const service = context.get.self.module('/service/funcionario');
service.consultar(123);
//// chamando um modulo que precisa do context
// /service/funcionario.js
module.exports = (context) => {
const consultar = function(id) {
const logger = context.get.server('logger');
logger.info('executando a consulta');
return 123;
}
return { consultar };
}
// /controller.js
const service = context.get.self.context.module('/service/funcionario');
service.consultar(123);
Foi gerado 3 scripts yaml para rodar com o docker-compose
- stack-db.yaml: Esse script baixa e executa o MongoDB e o Redis configurado para uma melhor utilização no desenvolvimento do projeto. Utilizando esse script, é necessario baixar as dependencias e executar o server em Node.js;
- stack-full.yaml: Esse script baixa e executa o MongoDB, Redis, baixa o Node.js e cria uma imagem e container com o server dentro. Utiliza as configurações para execução em produção;
- docker-compose.yaml (script principal): Esse script baixa baixa o Node.js e cria uma imagem e container com o server dentro;
Eu criei esses scripts para ajudar em cada fase do desenvolvimento.
$ docker-compose -f ./stack-full.yaml up
Primeiro vamos iniciar a imagem e o container do MongoDB e do Redis, utilizando um script que esta na raiz do projeto chamado de stack-db.yaml.
$ docker-compose -f ./stack-db.yaml up
Feito isso ele vai subir uma instancia do MongoDB, e vai armazenar no volume do Docker. Assim, a proxima vez que for subir o script do docker-compose, os dados estarão persistidos.
Para excluir os dados do volume do Docker:
$ docker-compose -f ./stack-db.yaml down -v
Como todo projeto em Node.js, é necessario instalar as dependencias antes:
$ cd ./src
$ npm i
E na sequencia, para iniciar o motor é necessario executar:
$ npm start
Ao executar, vai aparecer no console conforme a demonstração abaixo:
[info] 2018-09-12 20.34.15.299 express-cache - Redis (cache) ativado com sucesso na url: redis://localhost:6379
[info] 2018-09-12 20.34.15.316 express-mongodb - MongoDB ativado com sucesso na url: mongodb://localhost:27017
[debug] 2018-09-12 20.34.16.488 graphql-funcionarios - Foi solicitado o modulo "services/funcionario-service".
[debug] 2018-09-12 20.34.16.489 graphql-funcionarios - Foi solicitado o modulo "utils/mongodb-crud".
[debug] 2018-09-12 20.34.16.490 graphql-funcionarios - Foi solicitado a variavel "mongodb" do servidor.
[debug] 2018-09-12 20.34.16.490 graphql-funcionarios - Foi solicitado o modulo "modules/validador-opcional".
[debug] 2018-09-12 20.34.16.491 graphql-funcionarios - Foi solicitado o modulo "utils/validator".
[debug] 2018-09-12 20.34.16.491 graphql-funcionarios - Foi solicitado o modulo "modules/validador-insert".
[debug] 2018-09-12 20.34.16.492 graphql-funcionarios - Foi solicitado o modulo "utils/validator".
[debug] 2018-09-12 20.34.16.492 graphql-funcionarios - Foi solicitado o modulo "modules/validador-update".
[debug] 2018-09-12 20.34.16.493 graphql-funcionarios - Foi solicitado o modulo "utils/validator".
[debug] 2018-09-12 20.34.16.493 graphql-funcionarios - Foi solicitado o modulo "utils/cache-crud".
[debug] 2018-09-12 20.34.16.494 graphql-funcionarios - Foi solicitado a variavel "cache" do servidor.
[info] 2018-09-12 20.34.16.518 server - Executando "api-core-js@2.7.1" em http://localhost:3000 (develop) (pid:32632)
[debug] 2018-09-12 20.34.16.519 server - GraphQL IDE disponivel em http://localhost:3000/graphql
[debug] 2018-09-12 20.34.16.519 server - REST registrado....: /v1/funcionarios [get]
[debug] 2018-09-12 20.34.16.519 server - REST registrado....: /v1/funcionarios/:_id [delete]
[debug] 2018-09-12 20.34.16.520 server - REST registrado....: /v1/funcionarios/:_id [get]
[debug] 2018-09-12 20.34.16.520 server - REST registrado....: /v1/funcionarios/:id [put]
[debug] 2018-09-12 20.34.16.521 server - REST registrado....: /v1/funcionarios [post]
[debug] 2018-09-12 20.34.16.521 server - GraphQL registrado.: obterFuncionario
[debug] 2018-09-12 20.34.16.522 server - GraphQL registrado.: criarFuncionario
[debug] 2018-09-12 20.34.16.522 server - GraphQL registrado.: atualizarFuncionario
[debug] 2018-09-12 20.34.16.523 server - GraphQL registrado.: removerFuncionario
[debug] 2018-09-12 20.34.16.523 server - GraphQL registrado.: pesquisarFuncionarios
[info] 2018-09-12 20.34.16.525 health-check - Rota registrada: http://localhost:3001 (pid:32632)
Os exemplos abaixo são executados no GraphiQL (interface grafica, disponivel apenas em desenvolvimento). GraphiQL disponivel na rota: http://localhost:3000/graphiql
# Inclusao de funcionario
mutation example1 {
criarFuncionario(
nome: "Douglas"
sobrenome: "Silva"
cidade: "São Paulo"
estado: "SP"
telefone: "1170707070"
email: "dougla.silva@enterprise.com"
) {
_id
nome
sobrenome
}
}
# Listar funcionarios
query example2 {
listarFuncionarios {
_id
nome
cidade
}
}
# Pesquisar funcionarios
query example3 {
pesquisarFuncionarios (
estado: "SP"
){
_id
nome
cidade
}
}
# Consultar funcionarios pelo ID
query example4 {
obterFuncionario (_id: ?) {
nome
cidade
}
}
# Atualizar funcionario
mutation example5 {
atualizarFuncionario(
_id: ?
cidade: "Maceio"
estado: "Alagoas"
}) {
nome
cidade
}
}
# Excluir funcionario
mutation example6 {
excluirFuncionario(
_id: ?
})
}
Os exemplos abaixos são executados via POSTMAN:
# Inclusão de funcionario
- rota: [POST] http://localhost:3000/v1/funcionarios
payload:
{
"nome": "Douglas",
"sobrenome": "Silva",
"cidade": "São Paulo",
"estado": "SP",
"telefone": "1170707070",
"email": "dougla.silva@enterprise.com"
}
# Listar funcionarios
- rota: [GET] http://localhost:3000/v1/funcionarios
# Obter funcionario especifico
- rota: [GET] http://localhost:3000/v1/funcionarios/:_id
# Atualizar funcionario especifico
- rota: [PUT] http://localhost:3000/v1/funcionarios/:_id
payload:
{
"nome": "Douglas",
"sobrenome": "Silva",
"cidade": "Maceio",
"estado": "AL",
"telefone": "1170707070",
"email": "dougla.silva@enterprise.com"
}
# Excluir funcionario especifico
- rota: [DELETE] http://localhost:3000/v1/funcionarios/:_id