Access individual fields from request body inside cloud function - node.js

I'm sending a post request to a cloud function which contains the following body:
{message: "this is the message"}
If I try to print the entire body of the request, it shows it, but if I try to get the message field, I get undefined in the console.
Here's my function:
exports.myCloudFunction = functions.https.onRequest((req: any, res: any) => {
console.log(req.body)\\prints the body of the request fine
console.log(req.body.message)\\prints "undefined"
cors(req, res, () => {
const pl = req.body.message;
console.log(pl);\\prints "undefined"
});
return res.send("all done")
});

You don't need a body parser for Cloud Functions as described in the other answer here. Cloud Functions will automatically parse JSON and put the parsed JSON object in the body attribute. If you want this to happen automatically, you should set the content type of the request to "application/json" as described in the linked documentation. Then you can use req.body as you'd expect.

I personally haven't worked with firebase before, but from your code and the fact that req.body prints the body, it seems that you probably need to parse the request-body as json in order to be able to access the message property:
const body = JSON.parse(req.body);
console.log(body.message);
It could also be the case that you need to setup a bodyparser for json content. In a normal express-app you can do this using (note that you don't need the manual parsing from above anymore using bodyparser):
const bodyParser = require('body-parser');
app.use(bodyParser.json();
EDIT:
see Doug's answer for the correct way to do this, i.e. to fix your request and set the content-type to application/json in order for Cloud Functions to automatically parse and populate req.body with the request body.

You need to just convert the request body into a JSON object in cloud function.
const body = JSON.parse(req.body);
// message
console.log(body.message)

Related

validateRequest method in Node.js Twilio client library not validating request while running in AWS Lambda

I am trying to validate that an http POST request to an AWS Lamdbda function URL from a Twilio HTTP Request widget inside a Twilio Studio flow truly originated from Twilio. I am using the Node.js Twilio client library, which provides the validateRequest method to accomplish what I am after. The content-type header in the Twilio Studio flows HTTP Request widget is set to application/json
The problem is that I am passing in the "x-twilio-signature" header, the url, twilio auth token, and POST params to the validateRequest method and it always returns false. Below is the code snippet used to try and accomplish this.
const authToken = process.env.twilio_auth_token
const sid = process.env.twilio_account_sid
const client = require('twilio')
exports.handler = (event) =>
{
let twilioSignature = event.headers['x-twilio-signature']
let requestBody = event.body
let requestUrl = 'https://my-function-url.io/'
let requestIsValid = client.validateRequest(authToken, twilioSignature, requestUrl, requestBody)
if(requestIsValid){
console.log('valid request')
} else {
console.log('invalid request')
}
}
Seems like someone else had a similar issue in the past. I copied parts of the answer here:
The issue here is that query string parameters are treated differently to POST body parameters when generating the signature.
Notably part 3 of the steps used to generate the request signature says:
If your request is a POST, Twilio takes all the POST fields, sorts them by alphabetically by their name, and concatenates the parameter name and value to the end of the URL (with no delimiter).

Parsing request body prohibits request signature verification

I'm trying to build a serverless Slackbot using Lambda function. I end up with an error while verifying the Request URL through the Slack event API. #slack/events-api is the dependency that I'm using to capture the slack events.
Here is my code.
const sls = require('serverless-http');
const { createEventAdapter } = require('#slack/events-api');
require('dotenv').config();
const { SLACK_SIGNING_SECRET } = process.env
const slackEvents = createEventAdapter( SLACK_SIGNING_SECRET || '' );
slackEvents.on('message', async event => {
console.log('received!')
});
module.exports.server = sls(slackEvents.requestListener());
This is the error that I'm getting while verifing the request url
Slack Request URL verification
Can someone help me with this?
Just ran into this exact issue, and took a look into http-handler.js in node-slack-events.
All we have to do is store the raw request body as rawBody before serverless-http parses it.
serverless-http lets you transform the request, before it is sent to the app—great opportunity for a fix:
module.exports.handler = serverless(app, {
request(request) {
request.rawBody = request.body;
},
});
I'm not sure how to solve your problem exactly, but I do know what's causing it.
The library you're using, serverless-http parses the JSON body sent by Slack. This causes an error to be thrown, because the slack-api-sdk expects to parse the raw request body itself.
Could you try removing serverless-http and just respond to the API Gateway event?

Call Express router manually

Нello! I am looking to call a function which has been passed to an expressRouter.post(...) call.
This expressRouter.post(...) call is occurring in a file which I am unable to modify. The code has already been distributed to many clients and there is no procedure for me to modify their versions of the file. While I have no ability to update this file for remote clients, other developers are able to. I therefore face the issue of this POST endpoint's behaviour changing in the future.
I am also dealing with performance concerns. This POST endpoint expects req.body to be a parsed JSON object, and that JSON object can be excessively large.
My goal is to write a GET endpoint which internally activates this POST endpoint. The GET endpoint will need to call the POST endpoint with a very large JSON value, which has had URL query params inserted into it. The GET's functionality should always mirror the POST's functionality, including if the POST's functionality is updated in the future. For this reason I cannot copy/paste the POST's logic. Note also that the JSON format will never change.
I understand that the issue of calling an expressjs endpoint internally has conventionally been solved by either 1) extracting the router function into an accessible scope, or 2) generating an HTTP request to localhost.
Unfortunately in my case neither of these options are viable:
I can't move the function into an accessible scope as I can't modify the source, nor can I copy-paste the function as the original version may change
Avoiding the HTTP request is a high priority due to performance considerations. The HTTP request will require serializing+deserializing an excessively large JSON body, re-visiting a number of authentication middlewares (which require waiting for further HTTP requests + database queries to complete), etc
Here is my (contrived) POST endpoint:
expressRouter.post('/my/post/endpoint', (req, res) => {
if (!req.body.hasOwnProperty('val'))
return res.status(400).send('Missing "val"');
return res.status(200).send(`Your val: ${req.body.val}`);
});
If I make a POST request to localhost:<port>/my/post/endpoint I get the expected error or response based on whether I included "val" in the JSON body.
Now, I want to have exactly the same functionality available, but via GET, and with "val" supplied in the URL instead of in any JSON body. I have attempted the following:
expressRouter.get('/my/get/endpoint/:val', (req, res) => {
// Make it seem as if "val" occurred inside the JSON body
let fakeReq = {
body: {
val: req.params.val
}
};
// Now call the POST endpoint
// Pass the fake request, and the real response
// This should enable the POST endpoint to write data to the
// response, and it will seem like THIS endpoint wrote to the
// response.
manuallyCallExpressEndpoint(expressRouter, 'POST', '/my/post/endpoint', fakeReq, res);
});
Unfortunately I don't know how to implement manuallyCallExpressEndpoint.
Is there a solution to this problem which excludes both extracting the function into an accessible scope, and generating an HTTP request?
This seems possible, but it may make more sense to modify req and pass it, rather than create a whole new fakeReq object. The thing which enables this looks to be the router.handle(req, res, next) function. I'm not sure this is the smartest way to go about this, but it will certainly avoid the large overhead of a separate http request!
app.get('/my/get/endpoint/:val', (req, res) => {
// Modify `req`, don't create a whole new `fakeReq`
req.body = {
val: req.params.val
};
manuallyCallExpressEndpoint(app, 'POST', '/my/post/endpoint', req, res);
});
let manuallyCallExpressEndpoint = (router, method, url, req, res) => {
req.method = method;
req.url = url;
router.handle(req, res, () => {});
};
How about a simple middleware?
function checkVal(req, res, next) {
const val = req.params.val || req.body.val
if (!val) {
return res.status(400).send('Missing "val"');
}
return res.status(200).send(`Your val: ${val}`);
}
app.get('/my/get/endpoint/:val', checkVal)
app.post('/my/post/endpoint', checkVal)
This code isn't tested but gives you rough idea on how you can have the same code run in both places.
The checkVal function serves as a Express handler, with request, response and next. It checks for params first then the body.

Azure Function doesn't respond if Content-Type application/json

Introduction
I have running Azure Function, which is written as Expressjs application.
I have simple routes there to test
// Test get
router.get('/test', (req, res) => {
return res.status(200).send({ result: req.query });
});
// Test post
router.post('/test', (req, res) => {
return res.status(200).send({ result: req.body });
});
The app configuration is the following
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
Issue
I'm trying to do POST /test request.
With Content-Type: application/javascript it works well
With Content-Type: application/json it calls function, but nothing executes, and timeout is returned.
There is no issue with GET /test route.
There is no issue running locally.
Need help to understand why Azure Function call doesn't work properly for POST requests with Content-Type: application/json. Thank you
use :
app.use(bodyParser.json({ type: 'application/*+json' }));
to allow custom json types
Going deeper how Azure Function and body parser work, I figured out the root of the problem.
Be careful with request object
In regular expressjs app we have request object, which is actually instance of Stream object, and all incoming data goes through this streaming.
So, we have raw data, and we need to parse it right way. bodyParser middleware has several methods to parse this data from stream based on it's type (Content-Type).
For example bodyParser.json() method tries to parse data in JSON format with application/json content type. Once this function parses data req.body is fulfilled with JSON object.
Azure Functions don't support streaming
This is where everything breaks :) Yes, it doesn't support streaming. And since this is true data is accepted different way. So request object is no longer Stream object. And req.body is already fulfilled with incoming data. For application/json it contains JSON object, for other content types it contains raw data which is needed to be parsed. As example I can share my simple middleware code for parsing data with type x-www-form-urlencoded.
'use strict';
// https://github.com/sindresorhus/query-string
const queryString = require('query-string');
/**
* Azure body parser
*/
function azureBodyParser() {
return function(req, res, next) {
// x-www-form-urlencoded
if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
req.body = queryString.parse(req.body, { arrayFormat: 'bracket' });
}
next();
};
}
module.exports = azureBodyParser;
Then use it like this
const app = require('express')();
const azureBodyParser = require('./middlewares/azureBodyParser');
// ...
app.use(azureBodyParser());
You can change it and add more handlers for other content types.
Also we need to provide some condition whether we run our app on server or as Azure Function, it's possible using ENV variables for example. It's your home task :)
The problem
Since Azure Functions don't support streaming, bodyParser.json() tries to get data from stream, and this is a point where app stuck. As a result function execution ends up with timeout.
Hope it will be helpful for all who struggling with Azure... Good luck :)
Might be late to this thread . Had a similar issue migrating an express app to Azure Functions and had to consume x-form-urlencoded values .
Used
const qs = require('qs');
While evaluating the request checked if the requst has rawBody and the relevant headers
if(req.rawBody)
and then simply
parsedBody = qs.parse(req.rawBody);}
Hope this helps someone.
It is a pain to host an Express API as is due to the discontinued support in Azure Functions for express

ExpressJS: Getting parsed and raw body simultaneously

I'm trying to get both parsed body of a application/x-www-form-urlencoded post request and the raw body.
I found similar questions around here but non with a working answer.
I tried using raw-body but it ignores this kind of conetnet type.
Now I'm trying to use body-parser like so: app.use(bodyParser.urlencoded()); - it correctly populates the req.body but the question is how can I get the raw body as well?
Thanks
It is possible via bodyParser API to get the raw request body utilizing the verify function.
It is invoked before the stream is parsed with the raw body available as its third argument.
Note that it is a buffer, so you need to call toString if you want it as such:
app.use(bodyParser.urlencoded({
verify: function(req, res, body) {
req.rawBody = body.toString();
}
}));

Resources