Você está vendo a documentação do Apigee Edge.
Acesse a
documentação da Apigee X. informações
Edge Microgateway v. 2.5.x
Público-alvo
Este tópico é destinado a desenvolvedores que querem ampliar os recursos do Edge Microgateway criando plug-ins personalizados. Se quiser criar um novo plug-in, é necessário ter experiência com JavaScript e Node.js.
O que é um plug-in personalizado do Edge Microgateway?
Um plug-in é um módulo do Node.js que adiciona funcionalidade ao Edge Microgateway. Os módulos de plug-in seguem um padrão consistente e são armazenados em um local conhecido pelo Edge Microgateway, o que permite que eles sejam descobertos e executados automaticamente. Vários plug-ins predefinidos são fornecidos quando você instala o Edge Microgateway. Isso inclui plug-ins para autenticação, detenção de pico, cota e análise. Os plug-ins existentes são descritos em Usar plug-ins.
É possível adicionar novos recursos ao microgateway escrevendo plug-ins personalizados. Por padrão, o Edge Microgateway é essencialmente um proxy de passagem seguro que transmite solicitações e respostas inalteradas para e de serviços de destino. Com plug-ins personalizados, é possível interagir programaticamente com as solicitações e respostas que fluem pelo microgateway.
Onde colocar o código personalizado do plug-in
Uma pasta para plug-ins personalizados está incluída como parte da instalação do Edge Microgateway aqui:
[prefix]/lib/node_modules/edgemicro/node_modules/microgateway-plugins
em que [prefix]
é o diretório de prefixos npm
, conforme
descrito em "Onde o Edge Microgateway está instalado" em Como instalar o
Edge Microgateway.
Você pode alterar esse diretório de plug-ins padrão. Consulte Onde encontrar plug-ins.
Analisar os plug-ins predefinidos
Antes de tentar desenvolver seu próprio plug-in, verifique se nenhum dos plug-ins predefinidos atende aos seus requisitos. Esses plug-ins estão localizados em:
[prefix]/lib/node_modules/edgemicro/node_modules/microgateway-plugins
em que [prefix]
é o diretório de prefixos npm
. Consulte também "Onde o Edge Microgateway está instalado" em Como instalar o Edge Microgateway.
Para mais detalhes, consulte também Plug-ins predefinidos fornecidos com o Edge Microgateway.
Criar um plug-in simples
Nesta seção, vamos analisar as etapas necessárias para criar um plug-in simples. Esse plug-in substitui os dados de resposta (quaisquer que sejam) pela string "Hello, World!" e os imprime no terminal.
- Se o Edge Microgateway estiver em execução, interrompa-o agora:
edgemicro stop
-
cd
ao diretório do plug-in personalizado:cd [prefix]/lib/node_modules/edgemicro/plugins
em que
[prefix]
é o diretório de prefixosnpm
, conforme descrito em "Onde o Edge Microgateway está instalado" em Como instalar o Edge Microgateway. - Crie um novo projeto de plug-in chamado response-override e
cd
para ele:
mkdir response-override && cd response-override
- Crie um novo projeto Node.js:
npm init
Clique em "Return" várias vezes para aceitar os padrões. - Use um editor de texto para criar um novo arquivo com o nome
index.js
. - Copie o código a seguir no
index.js
e salve o arquivo.
'use strict'; var debug = require('debug') module.exports.init = function(config, logger, stats) { return { ondata_response: function(req, res, data, next) { debug('***** plugin ondata_response'); next(null, null); }, onend_response: function(req, res, data, next) { debug('***** plugin onend_response'); next(null, "Hello, World!\n\n"); } }; }
- Você criou um plug-in e precisa adicioná-lo à configuração do Edge Microgateway.
Abra o arquivo
$HOME/.edgemicro/[org]-[env]-config.yaml
, em queorg
eenv
são os nomes do ambiente e da organização do Edge. - Adicione o plug-in
response-override
ao elementoplugins:sequence
, conforme mostrado abaixo.
... plugins: dir: ../plugins sequence: - oauth - response-override ...
- Reinicie o Edge Microgateway.
- Chame uma API pelo Edge Microgateway. Essa chamada de API pressupõe que você tenha definido a mesma
configuração que o tutorial com a segurança da chave de API, conforme descrito em Como instalar
e configurar o Edge Microgateway:
curl -H 'x-api-key: uAM4gBSb6YoMvTHfx5lXJizYIpr5Jd' http://localhost:8000/hello/echo Hello, World!
Anatomia de um plug-in
O plug-in de amostra Edge Microgateway a seguir ilustra o padrão a ser seguido ao
desenvolver seus próprios plug-ins. O código-fonte do plug-in de amostra discutido nesta seção está no arquivo plugins/header-uppercase/index.js.
- Os plug-ins são módulos padrão do NPM com um
package.json
e umindex.js
na pasta raiz. - Um plug-in precisa exportar uma função init().
- A função init() usa três argumentos: config, logger e stats. Esses argumentos são descritos nos argumentos da função init() do plug-in.
- init() retorna um objeto com gerenciadores de funções nomeados que são chamados quando determinados eventos ocorrem durante o ciclo de vida de uma solicitação.
Funções do manipulador de eventos
Um plug-in precisa implementar algumas ou todas essas funções de manipulador de eventos. A implementação dessas funções fica a seu critério. Qualquer função é opcional, e um plug-in típico implementará pelo menos um subconjunto dessas funções.
Manipuladores de eventos de fluxo de solicitação
Essas funções são chamadas em eventos de solicitação no Edge Microgateway.
onrequest
ondata_request
onend_request
onclose_request
onerror_request
Função onrequest
Chamado no início da solicitação do cliente. Essa função é acionada quando o primeiro byte da solicitação é recebido pelo Edge Microgateway. Essa função dá acesso aos cabeçalhos da solicitação, ao URL, aos parâmetros de consulta e ao método HTTP. Se você chamar a seguir com um primeiro argumento verdadeiro (como uma instância de Erro), o processamento da solicitação será interrompido e uma solicitação de destino não será iniciada.
Exemplo:
onrequest: function(req, res, next) { debug('plugin onrequest'); req.headers['x-foo-request-start'] = Date.now(); next(); }
Função ondata_request
Chamado quando um bloco de dados é recebido do cliente. Transmite os dados da solicitação para o próximo plug-in na sequência dele. O valor retornado do último plug-in na sequência é enviado para o destino. Um caso de uso típico, mostrado abaixo, é transformar os dados da solicitação antes de enviá-los ao destino.
Exemplo:
ondata_request: function(req, res, data, next) { debug('plugin ondata_request ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }
Função onend_request
Chamado quando todos os dados da solicitação são recebidos do cliente.
Exemplo:
onend_request: function(req, res, data, next) { debug('plugin onend_request'); next(null, data); }
onclose_request
função
Indica que a conexão do cliente foi encerrada. Use essa função nos casos em que a conexão do cliente não é confiável. Ele é chamado quando a conexão de soquete com o cliente é fechada.
Exemplo:
onclose_request: function(req, res, next) { debug('plugin onclose_request'); next(); }
onerror_request
função
Chamado se houver um erro ao receber a solicitação do cliente.
Exemplo:
onerror_request: function(req, res, err, next) { debug('plugin onerror_request ' + err); next(); }
Manipuladores de eventos de fluxo de resposta
Essas funções são chamadas em eventos de resposta no Edge Microgateway.
onresponse
ondata_response
onend_response
onclose_response
onerror_response
onresponse
função
Chamado no início da resposta-alvo. Essa função é acionada quando o primeiro byte da resposta é recebido pelo Edge Microgateway. Essa função dá acesso aos cabeçalhos de resposta e ao código de status.
Exemplo:
onresponse: function(req, res, next) { debug('plugin onresponse'); res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start']) next(); }
Função ondata_response
Chamado quando um bloco de dados é recebido do destino.
Exemplo:
ondata_response: function(req, res, data, next) { debug('plugin ondata_response ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }
Função onend_response
Chamado quando todos os dados de resposta são recebidos do destino.
Exemplo:
onend_response: function(req, res, data, next) { debug('plugin onend_response'); next(null, data); }
onclose_response
função
Indica que a conexão de destino foi encerrada. Você pode usar essa função nos casos em que a conexão de destino não é confiável. Ele é chamado quando a conexão do soquete com o destino é fechada.
Exemplo:
onclose_response: function(req, res, next) { debug('plugin onclose_response'); next(); }
Função onerror_response
Chamado se houver um erro ao receber a resposta de destino.
Exemplo:
onerror_response: function(req, res, err, next) { debug('plugin onerror_response ' + err); next(); }
O que você precisa saber sobre as funções do manipulador de eventos do plug-in
As funções do manipulador de eventos do plug-in são chamadas em resposta a eventos específicos que ocorrem enquanto o Edge Microgateway processa uma determinada solicitação de API.
- Cada um dos gerenciadores de função init() (ondata_request, ondata_response etc.) precisa chamar o retorno de chamada next() quando o processamento é concluído. Se você não chamar next(), o processamento será interrompido e a solicitação travará.
- O primeiro argumento para next() pode ser um erro que fará com que o processamento da solicitação seja encerrado.
- Os gerenciadores ondata_ e onend_ precisam chamar next() com um segundo argumento contendo os dados a serem transmitidos para o destino ou cliente. Esse argumento poderá ser nulo se o plug-in estiver sendo armazenado em buffer e não tiver dados suficientes para transformar no momento.
- Uma única instância do plug-in é usada para atender a todas as solicitações e respostas. Se um plug-in quiser manter o estado por solicitação entre chamadas, ele poderá salvar esse estado em uma propriedade adicionada ao objeto request fornecido (req), cujo ciclo de vida é a duração da chamada de API.
- Capture todos os erros e chame next() com o erro. A falha em chamar next() resultará na chamada de API travando.
- Tenha cuidado para não introduzir vazamentos de memória, porque isso pode afetar o desempenho geral do Edge Microgateway e causar falhas se ele ficar sem memória.
- Tenha cuidado para acompanhar o modelo Node.js sem realizar tarefas com uso intensivo de computação na linha de execução principal, já que isso pode afetar negativamente o desempenho do Edge Microgateway.
Sobre a função init() do plug-in
Esta seção descreve os argumentos passados para a função init(): config, logger e stats.
config
Um objeto de configuração recebido após a mesclagem do arquivo de configuração do Edge Microgateway com
as informações que foram transferidas por download do Apigee Edge, como produtos e cotas. Você pode encontrar
configurações específicas do plug-in neste objeto: config.<plugin-name>
.
Para adicionar um parâmetro de configuração chamado param com um valor de foo
a um plug-in chamado response-override, coloque o seguinte no arquivo
default.yaml
:
response-override: param: foo
Em seguida, você pode acessar o parâmetro no código do plug-in da seguinte maneira:
// Called when response data is received ondata_response: function(req, res, data, next) { debug('***** plugin ondata_response'); debug('***** plugin ondata_response: config.param: ' + config.param); next(null, data); },
Nesse caso, você verá "foo" impresso na saída de depuração do plug-in:
Sun, 13 Dec 2015 21:25:08 GMT plugin:response-override ***** plugin ondata_response: config.param: foo
logger
O logger do sistema. O logger em uso atualmente exporta essas funções, em que o objeto pode ser uma string, uma solicitação HTTP, uma resposta HTTP ou uma instância de erro.
info(object, message)
warn(object, message)
error(object, message)
stats
Um objeto que contém contagens de solicitações, respostas, erros e outras estatísticas agregadas relacionadas às solicitações e respostas que fluem por uma instância de microgateway.
- treqErrors: o número de solicitações de destino com erros.
- treqErrors: o número de respostas de destino com erros.
- statusCodes: um objeto que contém contagens de códigos de resposta:
{ 1: number of target responses with 1xx response codes 2: number of target responses with 2xx response codes 3: number of target responses with 3xx response codes 4: number of target responses with 4xx response codes 5: number of target responses with 5xx response codes }
- solicitações: o número total de solicitações.
- responses: o número total de respostas.
- connections: o número de conexões de destino ativas.
Sobre a função next()
Todos os métodos do plug-in precisam chamar next()
para continuar o processamento do próximo método da série (ou o processo do plug-in travará). No ciclo de vida da solicitação, o primeiro método chamado é
onrequest(). O próximo método a ser chamado é o método ondata_request()
. No entanto,
ondata_request
é chamado apenas se a solicitação inclui dados, como no
caso, por exemplo, de uma solicitação POST. O próximo método chamado será onend_request()
, que é chamado quando o processamento da solicitação é concluído. As
funções onerror_*
são chamadas apenas em caso de erro e permitem que você
resolva os erros com código personalizado, se quiser.
Digamos que os dados sejam enviados na solicitação e que ondata_request()
seja chamado. Observe
que a função chama next()
com dois parâmetros:
next(null, data);
Por convenção, o primeiro parâmetro é usado para transmitir informações de erro, que podem ser processadas em uma função subsequente na cadeia. Ao defini-lo como null
, um argumento
falso, você confirma que não há erros e que o processamento da solicitação deve prosseguir normalmente. Se esse argumento for verdadeiro (como um objeto Error), o processamento da solicitação será interrompido, e a solicitação será enviada para o destino.
O segundo parâmetro transmite os dados da solicitação para a próxima função na cadeia. Se você não fizer nenhum processamento adicional, os dados da solicitação serão transmitidos inalterados para o destino da API.
No entanto, você tem a chance de modificar os dados da solicitação nesse método e transmitir a solicitação
modificada para o destino. Por exemplo, se os dados da solicitação são XML e o destino espera JSON, é possível adicionar um código ao método ondata_request()
que (a) muda o Content-Type do cabeçalho da solicitação para application/json
e converte os dados da solicitação para JSON usando o que você quer. Por exemplo, é possível usar um conversor xml2json do Node.js extraído do NPM.
Vamos conferir o resultado:
ondata_request: function(req, res, data, next) { debug('****** plugin ondata_request'); var translated_data = parser.toJson(data); next(null, translated_data); },
Nesse caso, os dados da solicitação (que presume ser XML) são convertidos em JSON, e os
dados transformados são transmitidos por next()
para a próxima função na cadeia de solicitação,
antes de serem transmitidos para o destino do back-end.
Observe que é possível adicionar outra instrução de depuração para imprimir os dados transformados para fins de depuração. Exemplo:
ondata_request: function(req, res, data, next) { debug('****** plugin ondata_request'); var translated_data = parser.toJson(data); debug('****** plugin ondata_response: translated_json: ' + translated_json); next(null, translated_data); },
Sobre a ordem de execução do gerenciador de plug-ins
Se você criar plug-ins para o Edge Microgateway, precisará entender a ordem em que os manipuladores de eventos do plug-in são executados.
É importante lembrar que, quando você especifica uma sequência de plug-ins no arquivo de configuração do Edge Microgateway, os gerenciadores de solicitação são executados em ordem crescente, enquanto os gerenciadores de resposta são executados em ordem decrescente.
O exemplo a seguir foi projetado para ajudar você a entender essa sequência de execução.
1. Criar três plug-ins simples
Considere o plug-in a seguir. Ele só mostra a saída do console quando os manipuladores de eventos são chamados:
plugins/plugin-1/index.js (em inglês)
module.exports.init = function(config, logger, stats) { return { onrequest: function(req, res, next) { console.log('plugin-1: onrequest'); next(); }, onend_request: function(req, res, data, next) { console.log('plugin-1: onend_request'); next(null, data); }, ondata_response: function(req, res, data, next) { console.log('plugin-1: ondata_response ' + data.length); next(null, data); }, onend_response: function(req, res, data, next) { console.log('plugin-1: onend_response'); next(null, data); } }; }
Agora, considere criar mais dois plug-ins, plugin-2
e plugin-3
, com o mesmo código (exceto, alterar as instruções console.log()
para plugin-2
e plugin-3
, respectivamente).
2. Revisar o código do plug-in
As funções de plug-in exportadas em <microgateway-root-dir>/plugins/plugin-1/index.js
são manipuladores de eventos que são executados em momentos específicos durante o processamento de solicitação e resposta. Por exemplo, onrequest
executa o primeiro byte dos cabeçalhos da solicitação que é recebido. Enquanto,
onend_response
é executado depois que o último byte dos dados de resposta é recebido.
Confira o gerenciador ondata_response. Ele é chamado sempre que um bloco dos dados de resposta é recebido. O importante é que os dados de resposta não sejam necessariamente recebidos de uma só vez. Em vez disso, os dados podem ser recebidos em blocos de tamanho arbitrário.
3. Adicionar os plug-ins à sequência de plug-ins
Continuando com esse exemplo, vamos adicionar os plug-ins à sequência de plug-ins no arquivo de configuração
do Edge Microgateway (~./edgemicro/config.yaml
) da seguinte maneira. A sequência é importante. Ele define a ordem em que os gerenciadores de plug-in são executados.
plugins: dir: ../plugins sequence: - plugin-1 - plugin-2 - plugin-3
4. Examinar a saída de depuração
Agora, vamos conferir a saída que seria produzida quando esses plug-ins fossem chamados. Há alguns pontos importantes a serem observados:
- A sequência do plug-in do arquivo de configuração do Edge Microgateway
(
~./edgemicro/config.yaml
) especifica a ordem em que os manipuladores de eventos são chamados. - Os gerenciadores de solicitações são chamados em ordem crescente (a ordem em que aparecem na sequência do plug-in: 1, 2, 3).
- Os gerenciadores de resposta são chamados em ordem decrescente: 3, 2, 1.
- O gerenciador
ondata_response
é chamado uma vez para cada bloco de dados que chega. Neste exemplo (saída mostrada abaixo), dois blocos são recebidos.
Este é um exemplo de saída de depuração produzido quando esses três plug-ins estão em uso e uma solicitação é enviada pelo Edge Microgateway. Observe a ordem em que os manipuladores são chamados:
plugin-1: onrequest plugin-2: onrequest plugin-3: onrequest plugin-1: onend_request plugin-2: onend_request plugin-3: onend_request plugin-3: ondata_response 931 plugin-2: ondata_response 931 plugin-1: ondata_response 931 plugin-3: ondata_response 1808 plugin-3: onend_response plugin-2: ondata_response 1808 plugin-2: onend_response plugin-1: ondata_response 1808 plugin-1: onend_response
Resumo
Entender a ordem em que os gerenciadores de plug-in são chamados é muito importante ao tentar implementar uma funcionalidade de plug-in personalizada, como acumular e transformar dados de solicitação ou resposta.
Lembre-se de que os gerenciadores de solicitações são executados na ordem em que os plug-ins são especificados no arquivo de configuração do Edge Microgateway, e os gerenciadores de resposta são executados na ordem oposta.
Sobre o uso de variáveis globais em plug-ins
Todas as solicitações para o Edge Microgateway são enviadas para a mesma instância de um plug-in. Portanto, o estado de uma segunda solicitação de outro cliente substituirá o primeiro. O único local seguro para salvar o estado do plug-in é armazenar o estado em uma propriedade no objeto de solicitação ou resposta (cujo ciclo de vida é limitado ao da solicitação).
Reescrever URLs de destino em plug-ins
Adicionado em: v2.3.3
Você pode modificar dinamicamente o URL de destino padrão em um plug-in modificando estas variáveis no código do plug-in: req.targetHostname e req.targetHostname.
Adicionado em: v2.4.x
Também é possível substituir a porta do endpoint de destino e escolher entre HTTP e HTTPS. Modifique essas variáveis no código do plug-in: req.targetPort e req.targetSecure. Para escolher HTTPS, defina req.targetSecure como true. Para HTTP, defina como false. Se você definir req.targetSecure como "true", consulte esta conversa para mais informações.
Plug-ins de exemplo
Esses plug-ins são fornecidos com a instalação do Edge Microgateway. É possível encontrá-las na instalação do Edge Microgateway aqui:
[prefix]/lib/node_modules/edgemicro/plugins
em que [prefix]
é o diretório de prefixos npm
, conforme
descrito em "Onde o Edge Microgateway está instalado" em Como instalar o
Edge Microgateway.
solicitação de acumulação
Esse plug-in acumula blocos de dados do cliente em uma propriedade de matriz anexada ao objeto de solicitação. Quando todos os dados da solicitação são recebidos, a matriz é concatenada em um buffer que é então passado para o próximo plug-in na sequência. Esse plug-in precisa ser o primeiro na sequência para que os plug-ins subsequentes recebam os dados de solicitação acumulados.
module.exports.init = function(config, logger, stats) { function accumulate(req, data) { if (!req._chunks) req._chunks = []; req._chunks.push(data); } return { ondata_request: function(req, res, data, next) { if (data && data.length > 0) accumulate(req, data); next(null, null); }, onend_request: function(req, res, data, next) { if (data && data.length > 0) accumulate(req, data); var content = null; if (req._chunks && req._chunks.length) { content = Buffer.concat(req._chunks); } delete req._chunks; next(null, content); } }; }
acumular resposta
Esse plug-in acumula blocos de dados do destino em uma propriedade de matriz anexada ao objeto de resposta. Quando todos os dados da resposta são recebidos, a matriz é concatenada em um buffer que é então passado para o próximo plug-in na sequência. Como esse plug-in opera em respostas, que são processadas na ordem inversa, coloque-o como o último plug-in na sequência.
module.exports.init = function(config, logger, stats) { function accumulate(res, data) { if (!res._chunks) res._chunks = []; res._chunks.push(data); } return { ondata_response: function(req, res, data, next) { if (data && data.length > 0) accumulate(res, data); next(null, null); }, onend_response: function(req, res, data, next) { if (data && data.length > 0) accumulate(res, data); var content = Buffer.concat(res._chunks); delete res._chunks; next(null, content); } }; }
plug-in de cabeçalho com letras maiúsculas
As distribuições do Edge Microgateway incluem um plug-in de amostra chamado
<microgateway-root-dir>/plugins/header-uppercase
. A amostra inclui comentários que descrevem cada um dos gerenciadores de função. Este exemplo faz uma transformação de dados simples da resposta de destino e adiciona cabeçalhos personalizados à solicitação do cliente e à resposta de destino.
Confira o código-fonte de
<microgateway-root-dir>/plugins/header-uppercase/index.js
:
'use strict'; var debug = require('debug')('plugin:header-uppercase'); // required module.exports.init = function(config, logger, stats) { var counter = 0; return { // indicates start of client request // request headers, url, query params, method should be available at this time // request processing stops (and a target request is not initiated) if // next is called with a truthy first argument (an instance of Error, for example) onrequest: function(req, res, next) { debug('plugin onrequest'); req.headers['x-foo-request-id'] = counter++; req.headers['x-foo-request-start'] = Date.now(); next(); }, // indicates start of target response // response headers and status code should be available at this time onresponse: function(req, res, next) { debug('plugin onresponse'); res.setHeader('x-foo-response-id', req.headers['x-foo-request-id']); res.setHeader('x-foo-response-time', Date.now() - req.headers['x-foo-request-start']); next(); }, // chunk of request body data received from client // should return (potentially) transformed data for next plugin in chain // the returned value from the last plugin in the chain is written to the target ondata_request: function(req, res, data, next) { debug('plugin ondata_request ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }, // chunk of response body data received from target // should return (potentially) transformed data for next plugin in chain // the returned value from the last plugin in the chain is written to the client ondata_response: function(req, res, data, next) { debug('plugin ondata_response ' + data.length); var transformed = data.toString().toUpperCase(); next(null, transformed); }, // indicates end of client request onend_request: function(req, res, data, next) { debug('plugin onend_request'); next(null, data); }, // indicates end of target response onend_response: function(req, res, data, next) { debug('plugin onend_response'); next(null, data); }, // error receiving client request onerror_request: function(req, res, err, next) { debug('plugin onerror_request ' + err); next(); }, // error receiving target response onerror_response: function(req, res, err, next) { debug('plugin onerror_response ' + err); next(); }, // indicates client connection closed onclose_request: function(req, res, next) { debug('plugin onclose_request'); next(); }, // indicates target connection closed onclose_response: function(req, res, next) { debug('plugin onclose_response'); next(); } }; }
transformar-maiúsculas
Este é um plug-in de transformação geral que pode ser modificado para fazer qualquer tipo de transformação que você quiser. Neste exemplo, simplesmente transformamos os dados de resposta e solicitação em maiúsculas.
*/ module.exports.init = function(config, logger, stats) { // perform content transformation here // the result of the transformation must be another Buffer function transform(data) { return new Buffer(data.toString().toUpperCase()); } return { ondata_response: function(req, res, data, next) { // transform each chunk as it is received next(null, data ? transform(data) : null); }, onend_response: function(req, res, data, next) { // transform accumulated data, if any next(null, data ? transform(data) : null); }, ondata_request: function(req, res, data, next) { // transform each chunk as it is received next(null, data ? transform(data) : null); }, onend_request: function(req, res, data, next) { // transform accumulated data, if any next(null, data ? transform(data) : null); } }; }