Criando APIs incríveis usando a library node-restful
Como de costume escrevo sobre minhas vivências, neste post irei falar sobre uma experiência fantástica que tive em um projeto.
Surgiu um projeto de uma api restful utilizando o mongodb, como tive uma experiência com o framework LoopBack pensei em usá-lo devido o tempo que teria, resolvi da uma pesquisada sobre a existência de algo diferente e que por sinal cheguei em uma library linda que me chamou muito atenção pela simplicidade e flexibilidade.
Node-restful
É uma biblioteca para fornecer rapidamente uma API REST
com express. Com ela você registra recursos de mongoose e as rotas RESTful
padrão são feitas automaticamente.
const express = require('express'),
restful = require('node-restful'),
mongoose = restful.mongoose;
const app = express()
const Resource = restful.model('resource', mongoose.Schema({
title: 'string',
year: 'number',
})
.methods(['get', 'post', 'put', 'delete'])
Resource.register(app, '/resources')
app.listen(3000)
A melhor parte é que
restful.model
retorna um modelo deMongoose
, para que você possa interagir com ele da forma que você já está acostumado ou seja,new Resource
,Resource.findById
etc.
GET /resources
GET /resources/:id
POST /resources
PUT /resources/:id
DELETE /resources/:id
Existem algumas funções disponíveis depois de registrar o esquema de mongoose.
.methods([…])
Há uma lista de métodos que devem estar disponíveis no recurso. Caso deseje não autorizar algum método apenas registre as que serão utilizadas.
Resource.methods(['get', 'post', 'put'])
Também podemos executar rotas personalizadas:
.route(path, handler)
Resource.route('recommend', (req, res, next) => {
res.send('I have a recommendation for you!')
})
Ou fazer combinações de métodos HTTP
; Suponhamos que seja necessário executar códigos antes ou depois de uma rota, e que seja necessário obter um dado(senha) antes de uma operação POST
ou PUT
.
Resource.before('post', hash_password).before('put', hash_password)
function hash_password(req, res, next) {
req.body.password = hash(req.body.password)
next()
}
Você poderá acessar variáveis locais em templates rendered no aplicativo. Isso é útil para fornecer funções auxiliares para templates, bem como dados de nível de aplicativo. As variáveis locais estão disponíveis no middleware via res.locals
:
res.locals.status_code => É o código de status retornado
res.locals.bundle => É o pacote de dados
Em todas as ligações antes e depois, você poderá modificar isso:
Resource.after('get', (req, res, next) => {
const tmp = res.locals.bundle.title // Permita trocar os campos do título e do ano
res.locals.bundle.title = res.locals.bundle.year
res.locals.bundle.year = tmp
next() // Não se esqueça de ligar para a próxima!
})
Resource.after('recommend', do_something) // Executa após todos os verbos HTTP
Também podemos definir rotas detalhadas /resources/route_name
como:
Resource.route('moreinfo', {
detail: true,
handler: (req, res, next) => {
// req.params.id holds the resource's id
res.send("I'm at /resources/:id/moreinfo!")
}
})
Criando um exemplo simples
Antes de começar é necessário certificar-se que o MongoDB, Nodejs e o NPM estão instalados.
Criei um diretório com nome node-resfull.
$ mkdir node-restfull
$ cd node-restfull
Utilizei o npm init para inicializar o projeto (irá criar o package.json)
$ npm init -y
Instalei os módulos que utilizei no projeto
$ npm i --save-dev body-parser express mongoose node-restful lodash
Criei um diretório com nome api e dentro o category
$ mkdir api
$ cd api
$ mkdir category
Também o diretório com nome common dentro do api
$ mkdir common
Crei o arquivo errorHandler.js
dentro do common para tratamento de erros
const _ = require('lodash')
module.exports = (req, res, next) => {
const bundle = res.locals.bundle
if(bundle.errors) {
const errors = parseErrors(bundle.errors)
res.status(500).json({errors})
} else {
next()
}
}
const parseErrors = (nodeRestfulErrors) => {
const errors = []
_.forIn(nodeRestfulErrors, error => errors.push(error.message))
return errors
}
Adicionei o arquivo category.js
dentro do diretório api/category
const restful = require('node-restful')
const mongoose = restful.mongoose
const categorySchema = new mongoose.Schema({
description: { type: String },
createAt: { type: Date, default: Date.now },
})
module.exports = restful.model('Category', categorySchema)
Adicionei o arquivo categoryService.js
const Category = require('./category')
const errorHandler = require('../common/errorHandler')
Category.methods(['get', 'post', 'put', 'delete'])
Category.updateOptions({new: true, runValidators: true})
Category.after('post', errorHandler).after('put', errorHandler)
module.exports = Category
Criei o diretório config na raíz do projeto
$ mkdir config
Adicionei o arquivo routes.js
e database.js
dentro do config
routes.js
const express = require('express')
module.exports = (server) => {
const protectedApi = express.Router()
server.use('/api', protectedApi)
const Category = require('../api/category/categoryService')
Category.register(protectedApi, '/categorys')
}
database.js
const mongoose = require('mongoose')
mongoose.Promise = global.Promise
module.exports = mongoose.connect('mongodb://localhost/banco')
Na raíz do projeto criei o arquivo server.js
e loader.js
server.js
const port = 3004
const bodyParser = require('body-parser')
const express = require('express')
const server = express()
server.use(bodyParser.urlencoded({ extended: true}))
server.use(bodyParser.json())
require('./config/routes')(server)
server.listen(port, () => {
console.log(`BACKEND is running on port ${port}.`)
})
module.exports = server
loader.js
require('./server')
require('./config/database')
Prontinho! basta executar o comando abaixo
$ node loader.js
Para testar utilizei o Postman
GET localhost:3004/api/categorys
GET localhost:3004/api/categorys/:id
POST localhost:3004/api/categorys
PUT localhost:3004/api/categorys/:id
DELETE localhost:3004/api/categorys/:id
Conclusão
O fator que me levou a utilizá-la foi a flexibilidade de poder moldá-la como e quando desejei.
Espero que sirva de incentivo!