Portuguese (Português) translation by Erick Patrick (you can also view the original English article)

Bem-vindo à segunda parte da nossa série sobre geradores e Koa. Se você perdeu a primeira parte, pode lê-la aqui. Antes de começarmos com o processo de desenvolvimento, tenha certeza de ter instalado a versão 0.11.9, ou mais recente, do Node.js.
Nesta parte, começaremos a criar nossa API usando o Koa.js e você aprenderá sobre roteamento, compressão, registros de log, limitação de uso e manipulação de erros no Koa.js. Também usaremos o Mongo como nosso armazenamento de dados e aprenderemos rapidamente sobre a importação de dados para o Mongo, e o quão fácil é selecionar dados usando o Koa. Finalmente, veremos como depurar apps no Koa.
Entendendo o Koa
Koa tem mudanças substanciais por trás dos panos, fazendo uso das maravilhas dos geradores do EcmaScript 6. Fora a mudança de fluxo de controle, o Koa introduz seus próprios objetos customizados, como o this
, this.request
e othis.response
, que funcionam como uma camada de açúcar sintático sobre os objetos req
e res
do Node, dando-lhe acesso a vários métodos convenientes e vários getters/setters
.
Além das conveniências, o Koa também faz uma limpeza nos mediadores que, no Express, dependia de hacks horrorosos que quase sempre modificavam os objetos base. Também fornece uma melhor manipulação de fluxo de dados.
Espere, O Que É Um Mediador?
Um mediador é uma função plugável que adiciona ou remover alguma funcionalidade específica ao realizar alguma tarefa nos objetos de requisição/resposta do Node.js.
Mediadores do Koa
Essencialmente, um mediador do Koa é uma função geradora que retorna uma função geradora e que aceita uma outra função geradora. Geralmente, uma aplicação possui uma série de mediadores que são executados a cada requisição.
Além disso, um mediador deve retornar para o próximo mediador abaixo no fluxo de controle, caso seja executado por um mediador mais alto. Discutiremos mais sobre isso na seção de manipulação de erros.
Construindo Mediadores
Uma última coisa: Para adicionar um mediador à sua aplicação Koa, usaremos o método koa.use()
e prover uma função mediadora (geradora) como argumento. Exemplo: app.use(koa-logger)
adiciona o koa-logger
à lista de mediadores que nossa aplicação usa.
Construindo a Aplicação
Para começar com a API do dicionário, precisamos de conjunto funcional de definições. Para recriar esse cenário da vida real, decidimos ir com um conjunto de dados real. Baixamos a lista de definições da Wikipedia e carregamos no Mongo. O conjunto tem cerca de 700.000 palavras, uma vez que importamos somente a lista de definições em inglês. Cada registro (ou documento) consiste de uma palavra, seu tipo e seu significado. Você pode ler mais sobre o processo de importação no arquivo import.txt
do repositório.
Continuando com o processo de desenvolvimento, clone o repositório e verifique seu progresso, navegando pelos diferentes commits. Para clonar o repositório, use o comando a seguir:
$ git clone https://github.com/bhanuc/dictapi.git
Comecemos criando um servidor base do Koa:
var koa = require('koa'); var app = koa(); app.use(function *(next){ this.type = 'json'; this.status = 200; this.body = {'Welcome': 'Essa é uma aplicação Olá Mundo de nível 2!!'}; }); if (!module.parent) app.listen(3000); console.log('Olá Mundo executando em http://localhost:3000/');
Na primeira linha, importamos o Koa e salvamos uma instância na variável app
. Então, adicionamos um único mediador na linha 5, que é uma função geradora anônima que recebe a variável next
como parâmetro. Aqui, atribuímos o tipo e o estado da resposta, que é determinado automaticamente, mas também podemos configurá-lo manualmente. Finalmente, configuramos o corpo da resposta.
Já que configuramos o corpo de nosso primeiro mediador, ele marcará o fim de cada ciclo de requisição e não envolveremos qualquer outro mediador. Por último, iniciamos o servidor, executando seu método listen
e passando um número de porta como parâmetro.
Podemos iniciar o servidor através da execução do script, dessa forma:
$ npm install koa $ node --harmony index.js
Você pode ir direto para esse ponto, buscando o commit 6858ae0
:
$ git checkout 6858ae0
Adicionando Capacidade de Roteamento
Roteamento permite-nos redirecionar diferentes requisições a diferentes funções, baseado no tipo da requisição e na URL requisitada. Por exemplo, podemos querer responder a /login
diferentemente de signup
. Isso pode ser feito a partir da adição de um mediador, que verifica manualmente a URL da requisição recebida e executa as funções correspondentes. Ou, ao invés de escrever o mediador manualmente, podemos usar um mediador criado pela comunidade, também conhecido como módulo mediador.
Para adicionar a capacidade de roteamento à nossa aplicação, usaremos um módulo da comunidade chamado de koa-router
.
Para usar o koa-router
, modificaremos o código que já temos para algo parecido com isso:
var koa = require('koa'); var app = koa(); var router = require('koa-router'); var mount = require('koa-mount'); var handler = function *(next){ this.type = 'json'; this.status = 200; this.body = {'Welcome': 'Essa é uma aplicação Olá Mundo de nível 2!!'}; }; var APIv1 = new router(); APIv1.get('/all', handler); app.use(mount('/v1', APIv1.middleware())); if (!module.parent) app.listen(3000); console.log('Olá Mundo executando em http://localhost:3000/');
Nós importamos dois módulos aqui, onde a variável router
guarda o koa-router
e a variável mount
guarda o módulo koa-mount
, permitindo-nos usar o roteador em nossa aplicação Koa.
Na linha 6, definimos nossa função handler
, que é a mesma função de antes mas, dessa vez, demos um nome a ela. Na linha 12, guardamos uma instância do roteador em APIv1
e na linha 13 registrarmos nosso manipulador para requisições GET
na rota /all
.
Assim, todas as requisições retornarão "not found", exceto aquelas que foram feitas para localhost:3000/all
. Finalmente, na linha 15, usamos o mediador mount
, que nos fornece uma função gerador que pode ser passada para app.use()
.
Para ver esse passo em específico ou comparar sua aplicação, execute o seguinte comando no repositório clonado:
$ git checkout 8f0d4e8
Antes de executarmos nossa aplicação, precisamos instalar o koa-router
e o koa-mount
usando o npm
. De acordo com o aumento da complexidade da aplicação, o número de módulos/dependências também aumenta.
Para manter o controle de todas as informações relacionadas ao projeto e disponibilizar esses dados no npm
, guardamos toda a informação em um arquivo package.json
, incluindo todas as dependências. Você pode criar um arquivo package.json
manualmente ou usar a interface interativa de linha de comando, que abrimos com o comando $ npm init
.
{ "name": "koa-api-dictionary", "version": "0.0.1", "description": "koa-api-dictionary application", "main": "index", "author": { "name": "Bhanu Pratap Chaudhary", "email": "bhanu423@gmail.com" }, "repository": { "type": "git", "url": "https://github.com/bhanuc/dictapi.git" }, "license": "MIT", "engines": { "node": ">= 0.11.13" } }
Um arquivo package.json
bem simples deve ser parecido com o que temos acima.
Uma vez que o arquivo package.json
esteja presente, você pode salvar a dependência usando o comando a seguir:
$ npm install <package-name> --save
Por exemplo: Nesse caso, instalaremos os módulos usando o comando abaixo e registraremos as dependências no arquivo package.json
.
$ npm install koa-router koa-mount --save
Agora, você pode executar sua aplicação, usando $ node --harmony index.js
.
Você pode ler mais sobre o arquivo package.json
aqui.
Adicionando Rotas para a API do Dicionário
Começaremos criando duas rotas para a API, uma para obter todos os resultados em uma consulta rápida e uma segunda para obter todas as palavras combinantes (que é bem mais devagar na primeira vez).
Para manter controle sobre as coisas, colocaremos as funções da API em um diretório à parte, chamado de api
e em um arquivo api.js
, e importaremos esse último em nosso arquivo index.js
posteriormente.
var monk = require('monk'); var wrap = require('co-monk'); var db = monk('localhost/mydb'); var words = wrap(db.get('words')); /** * GET - todos os resultados. */ exports.all = function *(){ if(this.request.query.word){ var res = yield words.find({ word : this.request.query.word }); this.body = res; } else { this.response.status = 404; } }; /** * GET - um único resultado. */ exports.single = function *(){ if(this.request.query.word){ var res = yield words.findOne({ word : this.request.query.word }); this.body = res; } else { this.response.status = 404; } };
Aqui, usaremos o co-monk
, que age como um invólucro para o monk
, facilitando bastante o nosso trabalho de consultar o MongoDB usando geradores no Koa. Aqui, nós importamos o monk
e co-monk
, e conectamos à instância do MongoDB na linha 3. Invocamos o método wrap()
nas coleções, para torná-lo amigável a geradores.
Logo depois adicionamos dois métodos geradores, chamados all
e single
, como propriedades da variável exports
, de modo que eles possam ser importados por outros arquivos. Em cada uma dessas funções, primeiro verificamos o parâmetro da consulta, word
. Se ele estiver presente, realizamos a consulta e a retornamos. Caso contrário, lançamos um erro 404.
Usamos a palavra-chave yield
para esperar pelos resultados, assim como foi discutido no primeiro artigo, pausando a execução daquela função até que o resultado seja recebido. Na linha 12, usamos o método find
, que retorna todas as palavras combinantes, que são salvar em res
e, subsequentemente, enviadas de volta. Na linha 23, usamos o método findOne
disponível na coleção, que retorna o primeiro resultado combinante.
Atribuindo Os Manipuladores às Rotas
var koa = require('koa'); var app = koa(); var router = require('koa-router'); var mount = require('koa-mount'); var api = require('./api/api.js'); var APIv1 = new router(); APIv1.get('/all', api.all); APIv1.get('/single', api.single); app.use(mount('/v1', APIv1.middleware())); if (!module.parent) app.listen(3000); console.log('Dictapi Executando em http://localhost:3000/');
Aqui importamos os métodos exportados do arquivo api.js
e atribuímos os manipuladores às respectivas rotas GET
, /all
e /single
, e temos uma API completamente funcional e aplicação pronta, agora.
Para executar a aplicação, você só precisa instalar os módulos monk
e co-monk
usando o comando abaixo. Além disso, garanta a existência de uma instância do MongoDB executando, a qual você importou os dados, de acordo com as instruções mencionadas no arquivo import.txt
, presente no repositório do GitHub.
$ npm install monk co-monk --save
Agora, execute a aplicação usando o comando a seguir:
$ node --harmony index.js
Você pode abrir o navegador e abrir as seguintes URLS para verificar o funcionamento da aplicação. Apenas substitua a palavra 'new' por uma outra palavra que queira consultar.
http://localhost:3000/v1/all?word=new
http://localhost:3000/v1/single?word=new
Para chegar a esse passo rapidamente, execute o comando a seguir no diretório do repositório clonado:
$ git checkout f1076eb
Manipulação de Erros no Koa
Ao usar mediadores em cascata, podemos capturar erros usando o mecanismo try/catch
, uma vez que cada mediador pode responder enquanto repassa para os geradores abaixo e acima. Assim, se adicionarmos um mediador Try and Catch no começo da aplicação, ele capturará todos os erros encontrados pela requisição durante a passagem por todos os outros mediadores, uma vez que ele será o último mediador na volta dos geradores. Para tanto, basta adicionar o código a seguir na linha 10 do arquivo index.js
ou antes.
app.use(function *(next){ try{ yield next; // passa a execução para o próximo mediador da fila } catch (err) { //só executando quando ocorre erros & e nenhum outro mediador responde à requisição this.type = 'json'; //declaração opcional this.status = err.status || 500; this.body = { 'error' : 'A aplicação ficou louca. Torçamos que a NSA tenha todos os logs ;) '}; //delega o erro de volta para a aplicação this.app.emit('error', err, this); } });
Adicionando Registro de Log e Limitação de Uso na Aplicação
Registrar logs é uma parte essencial de uma aplicação moderna, uma vez que são bastante úteis na depuração e descoberta de problemas em nossas aplicações. Eles também guardam todas as atividades realizada e, dessa forma, podem ser usados para descobrir padrões de atividade de usuários e outros padrões interessantes.
Limitação de uso também se tornou uma parte essencial de aplicações modernos, onde é importante parar spammers e bots de drenarem recursos preciosos do servidor e pará-los de consumir sua API.
É bem simples de adicionar registro de logs e limitação de uso em uma aplicação Koa. Usaremos dois módulos da comunidade: koa-logger
ekoa-better-rate-limiting
. Precisamos adicionar o código a seguir à nossa aplicação:
var logger = require('koa-logger'); var limit = require('koa-better-ratelimit'); //e as linhas abaixo um pouco antes do mediador de erros app.use(limit({ duration: 1000*60*3 , // 3 minutos max: 10, blacklist: []})); app.use(logger());
Nessa parte, importamos os dois módulos e os adicionamos como mediador. O registrador de logs salvará e imprimirá na stdout
do processo que pode ser salva, facilmente em um arquivo. E o limitador de uso limita o número de requisições que um dado usuário pode fazer em um determinado período de tempo (o máximo de requisições aqui é de 10 em 3 minutos). Você também pode adicionar um vetor de endereços IP que pode ser bloqueadas e suas requisições não serão processadas.
Lembre-se de instalar os móduos antes de usá-los, usando o comando abaixo:
$ npm install koa-logger koa-better-ratelimit --save
Comprimindo o Tráfego
Uma das maneiras de garantir uma entrega rápida é usar gzip
em sua resposta, que é bem simples no Koa. Para comprimir o tráfego no koa, você pode usar o módulo koa-compress
.
Nesse caso, as opções podem ser um objeto vazio ou podem ser configurados de acordo com a necessidade.
var compress = require('koa-compress'); var opts = { filter: function (content_type) { return /text/i.test(content_type) }, // filtra as requisições a serem comprimidas usando regex threshold: 2048, //minimum size to compress flush: require('zlib').Z_SYNC_FLUSH }; } //use the code below to add the middleware to the application app.use(compress(opts));
Você pode até desligar a compressão em uma requisição adicionando o trecho de código a seguir ao mediador:
this.compress = true;
Não esqueça de instalar o módulo compress
através do npm
.
$ npm install compress --save
Para chegar a esse estágio e comparar à sua aplicação, execute o comando a seguir no diretório do repositório clonado:
git checkout 8f5b5a6
Escrevendo Testes
Testes devem ser uma parte essencial de qualquer código e devem objetivar o máximo de cobertura do código que for possível. Nesse artigo, criaremos os testes paras as rotas que são acessíveis em nossa aplicação. Usaremos o supertest e o Mocha para criar nossos testes.
Salvaremos nossos testes no arquivo test.js
que ficará dentro da pasta api
. Em ambos os casos, primeiro descreveremos nosso teste, dando-lhe um nome mais legível por humanos. Logo depois, passaremos uma função anônima que descreverá o comportamento correto do teste e levará uma função callback que conterá o teste de verdade. Em cada teste, importaremos nossa aplicação, iniciaremos o servidor, descreveremos o tipo de requisição, URL e consulta, e então passaremos a codificação para o gzip. Finalmente, verificaremos se a resposta está correta.
var request = require('supertest'); var api = require('../index.js'); describe('GET all', function(){ it('deveshould respond with all the words', function(done){ var app = api; request(app.listen()) .get('/v1/all') .query({ word: 'new' }) .set('Accept-Encoding', 'gzip') .expect('Content-Type', /json/) .expect(200) .end(done); }) }) describe('GET /v1/single', function(){ it('should respond with a single result', function(done){ var app = api; request(app.listen()) .get('/v1/single') .query({ word: 'new' }) .set('Accept-Encoding', 'gzip') .expect(200) .expect('Content-Type', /json/) .end(function(err, res){ if (err) throw err; else { if (!('_id' in res.body)) return "id faltando"; if (!('word' in res.body)) throw new Error("palavra faltando"); done(); } }); }) })
Para executar nosso teste, criaremos um arquivo Makefile
:
test: @NODE_ENV=test ./node_modules/.bin/mocha \ --require should \ --reporter nyan \ --harmony \ --bail \ api/test.js .PHONY: test
Nós configuramos o relator (nyan cat) e o framework de testes (mocha). Perceba que a importação deve adicionar o semáforo --harmony
para habilitar o modo ES6. Finalmente, também especificamos o local onde estão os testes. Um arquivo Makefile
configurado para testes infinitos da sua aplicação.
Agora, para testar sua aplicação, apenas execute o comando a seguir, no diretório principal da sua aplicação.
$ make test
Lembre-se de instalar os módulos de teste (mocha, should, supertest) antes de executar os testes, usando o comando abaixo:
$ npm install mocha should mocha --save-dev
Executando em Modo Produção
Para executar nossa aplicação em produção, usaremos o PM2, que é um monitor de processo para Node, muito útil. Nós devemos desabilitar o registrador de logs na produção. Isso pode ser automatizado através de variáveis de ambiente.
Para instalar o PM2, digite o comando a seguir no terminal
$ npm install pm2 -g
E nosso aplicativo pode ser iniciado, usando o comando a seguir:
$ pm2 start index.js --node-args="--harmony"
Agora, mesmo que nossa aplicação dê problemas, ela reiniciará automaticamente e você pode dormir sem problemas.
Conclusão
O Koa é um mediador leve e expressivo para o Node.js que torna o processo de criação de aplicações web e de APIs muito mais agradáveis.
Ele permite você lançar mão de uma enorme gama de módulos da comunidade para estender a funcionalidade de sua aplicação e simplificar as tarefas padrões, tornando o desenvolvimento para web uma atividade divertida.
Por favor, não hesite em deixar comentários, perguntas ou quaisquer outras informações logo abaixo.
Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post