Handle response payload with fastify-reply-from - fastify

My goal is to manipulate the response payload after the response already returned to the client, but it looks impossible doing it with fastify + fastify-reply-from without affecting the response time. Here is the only way I could get the response body, and do something with it.
fastify.addHook('onSend', async (req, reply, payload) => {
return new Promise((resolve, reject) => {
const chunks = [];
payload.on('error', reject);
payload.on('data', (chunk) => {
chunks.push(chunk);
});
payload.on('end', () => {
const body = Buffer.concat(chunks);
doSomethingWithBody(body);
resolve(body);
});
});
Are there other ways to do it, without handling manually the stream (payload is "IncomingMessage" instance)?

Related

Save response data in variable using node https.req

i am working with some Third Party api that for some reason ask to send body with GET request, weird but i don't have control over that. Node-Fetch is not allowing sending body with GET request so i am using https lib. My issue is that i am trying to save the response as a variable but get undefined over and over again. If i console.log the results i can see the response and its all great but i need the data as a variable.
var req = https.request(url, options, (res) => {
res.on("data", (chunk) => {
finalResponse += chunk
});
res.on('end', () => {
console.log(finalResponse); //Working
})
});
req.on("error", (e) => {
console.error(e);
});
req.write(postData);
req.end();
console.log('RESULTS: ' + finalResponse); //Undefined
It's undefined because Node.js runs asynchronously, so the result is logged before it's actually finished.
In order to get the data, you'll need to wait for the request to finish from your consumer, which is accomplished by using promises, i.e. using async/await, and promisifying the request method, which means making it return a promise to the consumer, and resolve it when the request is finished.
Also, response is usually a Buffer, and if you need string, you can convert it.
Try this:
function getFinalResponse() {
// return a Promise and resolve
// when the request is done
return new Promise((resolve, reject) => {
var req = https.request(url, options, (res) => {
const finalResponse = [];
res.on("data", (chunk) => {
finalResponse.push(chunk);
});
res.on('end', () => {
console.log(finalResponse); //Working
// if it's a string then you can use
// Buffer.concat(finalResponse).toString();
resolve(Buffer.concat(finalResponse));
})
});
req.on("error", (e) => {
console.error(e);
reject(e);
});
req.write(postData);
req.end();
});
}
// consumer
async function run() {
console.log('RESULTS: ', await getFinalResponse); //Undefined
}
// call the consumer
run();

In NodeJS, how do I await the response of the http2 client library GET call?

I'm using the http2 client package with nodeJS. I want to execute a simple get request, and await the response from the server. So far I have
import * as http2 from "http2";
...
const clientSession = http2.connect(rootDomain);
...
const req = clientSession.request({ ':method': 'GET', ':path': path });
let data = '';
req.on('response', (responseHeaders) => {
// do something with the headers
});
req.on('data', (chunk) => {
data += chunk;
console.log("chunk:" + chunk);
});
req.on('end', () => {
console.log("data:" + data);
console.log("end!");
clientSession.destroy();
});
process.exit(0);
But what I"m not able to figure out is how to do I await the response of the request before exiting? Right now the code flies through to the process.exit line and I can't see a way to block until the request is done.
If you want to await it, then you have to encapsulate it into a function that returns a promise and you can then use await on that promise. Here's one way to do that:
import * as http2 from "http2";
...
function getData(path) {
return new Promise((resolve, reject) => {
const clientSession = http2.connect(rootDomain);
const req = clientSession.request({ ':method': 'GET', ':path': path });
let data = '';
req.on('response', (responseHeaders) => {
// do something with the headers
});
req.on('data', (chunk) => {
data += chunk;
console.log("chunk:" + chunk);
});
req.on('end', () => {
console.log("data:" + data);
console.log("end!");
clientSession.destroy();
resolve(data);
});
req.on('error', (err) => {
clientSession.destroy();
reject(err);
});
});
}
async function run() {
let data = await getData(path);
// do something with data here
}
run().then(() => {
process.exit(0);
}).catch(err => {
console.log(err);
process.exit(1);
});
The other way to do this is to use a higher level http library that does much of this work for you. Here's an example using the got module:
import got from 'got';
async function run() {
let data = await got(url, {http2: true});
// do something with data here
}
In this case, the got() module already supports http2 for you (if you specify that option), already collects the whole response for you and already supports promises (all the things your code needed to add in your original version).
Note, the GET method is the default method which is why it is not necessary to specify it here.
response = await new Promise(async (resolve, reject)=> {
let data = '';
req.on('data', async (chunk) => {
data += chunk;
resolve(JSON.parse(data));
});
});

Using node core http and viewing response. Where is the response json?

I am getting a 201 statusCode by running this code in Node. I am unsure how to view the response body that I am seeing in Postman. The response object is so big and overwhelming and after many searches, I just do not know what is going on. How can I view the response body json? Look at Postman image below, that response is what I am talking about. res.body, res.json() does not exist.
const options = {
method: 'POST',
headers,
hostname: uploadHostName,
path: `/${uploadPath}`,
};
const req = https.request(options, res => {
res.on('data', data => console.error({data}));
res.on('error', error => console.error({error}));
res.on('end', () => {
const { statusCode, statusMessage } = res;
console.log({ statusCode, statusMessage });
});
});
const imageStream = fs.createReadStream(imageFile);
imageStream.pipe(req);
imageStream.on('end', () => {
console.log("READ_STREAM_END")
req.end();
});
const req = https.request(options, res => {
let chunks = [];
res.on('data', data => chunks.push(data));
res.on('error', error => console.error({error}));
res.on('end', () => {
const { statusCode, statusMessage, body } = res;
// console.log(Object.keys(res.client))
console.log({ statusCode, statusMessage, body });
console.log(Buffer.concat(chunks).toString());
});
});
I did not realize what the chunks were. For some reason, I was thinking this data was the uploaded file. I was also confused about that - why would it send the upload data to the res? Then It dawned on me after taking a short break. The res.on('data') is probably the strigified json I need!

Node JS - Cannot get stream data without pipe on promise-ftp

Hi guys I'm facing problem with my Node.js api with Express when I'm trying to get files from FTP and then send then over my API as base64.
I'm using -> promise-ftp (https://www.npmjs.com/package/promise-ftp).
This is how endpoint looks like:
getData = (req, res, next) => {
const ftp = new PromiseFtp();
let data = [];
ftp.connect({host: 'xxxl',user: 'xxx',password: 'xxx'})
.then(() => {
return ftp.get('xxx.pdf');
}).then((stream) => {
return new Promise((resolve, reject) => {
stream.once('close', resolve);
stream.once('error', reject);
stream.pipe(fs.createReadStream('test.pdf'));
stream
.on('error', (err) => {
return res.send({errorMessage: err});
})
.on('data', (chunk) => data.push(chunk))
.on('end', () => {
const buffer = Buffer.concat(data);
label = buffer.toString('base64');
return res.send(label);
});
});
}).then(() => {
return ftp.end();
});
}
The problem is that I don't want to save this file localy next to api files and when I remove line stream.pipe(fs.createReadStream('test.pdf')); it doesn't work.
I'm not sure what pipe is doing here.
May you please help me?
readable.pipe(writable) is part of Node's Stream API, which transparently writes the data that is read from the readable into the writable stream, handling backpressure for you. Piping the data to the filesystem is unnecessary, and Express Response object implements the writable stream interface so you could just pipe the stream returned from the FTP promise directly to the res object.
getData = async (req, res) => {
const ftp = new PromiseFtp();
try {
await ftp.connect({host: 'xxxl',user: 'xxx',password: 'xxx'});
const stream = await ftp.get('xxx.pdf');
res.type('pdf');
await new Promise((resolve, reject) => {
res.on('finish', resolve);
stream.once('error', reject);
stream.pipe(res);
});
} catch(e) {
console.error(e);
} finally {
await ftp.end();
}
}
If you don't have a Node version that supports async/await, here's a Promise-only version:
getData = (req, res) => {
const ftp = new PromiseFtp();
ftp
.connect({host: 'xxxl',user: 'xxx',password: 'xxx'})
.then(() => ftp.get('xxx.pdf'))
.then(stream => {
res.type('pdf');
return new Promise((resolve, reject) => {
res.on('finish', resolve);
stream.once('error', reject);
stream.pipe(res);
});
})
.catch(e => {
console.error(e);
})
.finally(() => ftp.end());
}
Here you have a good use-case for using a Promise's finally()-method or a try/catch/finally block, which will ensure that ftp.end() is called even if an error occurs or not.
Note that I've deliberately left out sending the error back to clients as doing such things could possibly leak sensitive information. A better solution is to setup proper server-side logging with request context.

Fetch and post text in NodeJS

I'm trying to grab text from an API that only returns a string of text ((here)) and having troubles throwing that out in a response. When posting, it comes out as [object Response], and the console.log doesn't show the text I want out of it.
The code I'm using:
fetch('http://taskinoz.com/gdq/api').then(
function(response) {
console.log(response);
throttledSendMessage(channel, response);
return response;
})
.catch(function(error) {
throttledSendMessage(channel, "An error has occured");
})
Log can be found here
Thanks for looking with me, couldn't find a solution :/
I think that because fetch returns a Response you need to call one of the functions on Response in order to get at the body's text. Here's an example:
fetch('https://github.com/')
.then(res => res.text())
.then(body => console.log(body));
Probably the problem is in async behavior of node.js. You can read more here
Also, I'm assume you use this package to make fetch request in node.js.
And assume that throttledSendMessage function is synchronous.
About your problem, just try to rewrite co de to use async/await for cleaner solution.
// We'll use IIFE function
(async () => {
const fetchResult = await fetch('http://taskinoz.com/gdq/api')
// Here fetch return the promise, so we need to await it again and parse according to our needs. So, the result code would be this
const data = await fetchResult.text();
throttledSendMessage(channel, data);
return data;
})()
Fetch is not available in nodejs , you could use the node-fetch https://www.npmjs.com/package/node-fetch , or you could use this fetch function:
const https = require('https');
const http = require('http');
function fetch(url, options = {}) {
return new Promise((resolve, reject) => {
if (!url) return reject(new Error('Url is required'));
const { body, method = 'GET', ...restOptions } = options;
const client = url.startsWith('https') ? https : http;
const request = client.request(url, { method, ...restOptions }, (res) => {
let chunks = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
chunks += chunk;
});
res.on('end', () => {
resolve({ statusCode: res.statusCode, body: chunks });
});
});
request.on('error', (err) => {
reject(err);
});
if (body) {
request.setHeader('Content-Length', body.length);
request.write(body);
}
request.end();
});
}
module.exports = fetch;
you could use it like this inside your code :
const result = await fetch(
`https://YOUR_URL`,
{
method: 'PUT',
body: JSON.stringify({ test: 'This is just a test', prio: 1 }),
},
);

Resources