I am using Node.js 8.10 in lambda proxy integration.
My goal is very simple.
send json data to lambda function
query rds with json data and retrive data from rds
return response with json data in lambda from rds data.
but I faced issue in step 1. I tried to figure out and I asked aws support center. it was not helpful. please help me.
my test json data are :
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
my code is very simple but have a problem :
exports.handler = async (event) => {
var body = JSON.parse(event)//<--this is a problem
let responseBody = {
message: "hello",
key1: body.key1
};
const response = {
statusCode: 200,
headers : {"Access-Control-Allow-Origin" : "*"},
body: JSON.stringify(responseBody)
};
return response;
};
I got this error in second line.
{"errorMessage":"Unexpected token o in JSON at position 1","errorType":"SyntaxError","stackTrace":["JSON.parse (<anonymous>)","exports.handler (/var/task/index.js:2:21)"]}
So I changed second line code like this.
var body = JSON.parse(JSON.stringify(event))
and then I got response well. I thought that's working well.
{
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*"
},
"body": "{\"message\":\"hello\",\"key1\":\"value1\"}"
}
but I sent same json data with postman for real. but I got only this.
{
"message": "hello"
}
there isn't "key1". so I added console.log and then I checked log in cloudwatch.
{ message: 'hello', key1: undefined }
I can see aws editor log is well but when I send data with postman. lambda couldn't parse my json data. it means lambda couldn't parse event parameter.
my questions are :
1. when I tried to parse like this
var body = JSON.parse(event)"
why does lambda editor issue error? Is there something wrong? this is super simple and very common code.
How can I parse my json data and return correct value not undefined. I expected this response
{ message: 'hello', key1: 'value1' }
I have been trying to solve this for 3days. but I really have no idea. please help me out.
The body coming from API Gateway is a stringified JSON, so you need to parse it and then access the attribute you want.
'use strict';
exports.handler = async (event) => {
let responseBody = {
message: "hello",
key1: JSON.parse(event.body).key1
};
const response = {
statusCode: 200,
headers: { "Access-Control-Allow-Origin": "*" },
body: JSON.stringify(responseBody)
};
return response;
};
This will do it.
you need to pass the parameters in postman as JSON.
in the body tab choose the 'raw' option and then from the combo box select 'JSON' type:
then just type :
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
and in your lambda function access to it like this:
JSON.parse(event.body).key1
Related
Trying to learn wit.ai and create a messenger bot via their example code from their github. after messing around and adding my own take, I encountered this error:
UnhandledPromiseRejectionWarning: Error: (#100) Param message[text]
must be a UTF-8 encoded string
I've tried using the UTF8 package (https://www.npmjs.com/package/utf8) but I don't think it resolves this issue. I believe this has also been asked years before (facebook messenger bot encoding error) but the solution provided there seemed to have been present in the original wit.ai sample code as of current yet I'm still getting the error.
This is the function where the error is thrown:
const fbMessage = (id, text) => {
const body = JSON.stringify({
recipient: { id },
message: { text },
});
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
The "v15.0/me/messages" API expects a message to either contain attachment or text.
Text:
{
"recipient": {"id": "some_valid_id"},
"message": {
"text": "some_string"
}
}
Or attachment, which expects following FB templates, which are in JSON:
{
"recipient": {"id": "some_valid_id"},
"message":{
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"example_title",
"subtitle":"example_subtitle",
"image_url":"some_url",
"buttons":[
{
"type":"postback",
"title":"Option1",
"payload":"opt1"
},
{
"type":"postback",
"title":"Option2",
"payload":"opt2"
}
]
}
]
}
}
}
}
Based on the above, it is either "text" for a simple string, or "attachment" for a rich message.
Returning back to your error. When using a value other than an encoded string for text, you will get the exact error:
UnhandledPromiseRejectionWarning: Error: (#100) Param message[text] must be a UTF-8
You will notice that this is what you are doing in your code, i.e using json with text instead of attachment.
I hope this will help fix the issue for you, but I fixed mine using what I mentioned above.
I read a lot of topics about how to handle POST requests with Lambda Proxy Integration from AWS, but no one of them helps me to solve my issue.
I read that if we are using a Lambda Proxy Integration the post body have to be like this :
{
"body": "{\"name\":\"Mickael2\"}"
}
Here is my Lambda function (very basic):
const AWS = require('aws-sdk')
const dynamoDB = new AWS.DynamoDB({ region: 'eu-west-3'})
const { v4: uuidv4 } = require('uuid');
exports.handler = (event, context, cb) => {
const body = JSON.parse(event.body).body;
const name = JSON.parse(body).name;
let uuid = uuidv4();
let response = {
statusCode: 200,
headers: {
'x-custom-header': 'My Header Value',
},
body: JSON.stringify({})
};
const params = {
Item: {
"id": {
S: uuid
},
"name": {
S: name
}
},
TableName: "Users3"
};
dynamoDB.putItem(params, function(err, data) {
if (err){
console.log(err, err.stack);
cb(null, response);
}
else{
console.log(data);
response.body = JSON.stringify({name:name, uuid: uuid })
cb(null, response);
}
});
};
For example when i am trying to give this body:
{
"name":"Mickael"
}
And doing console.log(event.body) i am getting:
{
"name": "Mickael" }
But when I am trying to get access to event.body.name i am getting undefined
Does someone have any other way to work with Post request on Lambda Proxy Integration? Thanks.
I see that you are using the Lambda function to push into DynamoDB. Since you're asking about other ways to handle it, if this Lambda function isn't doing anything else (and assuming you're using API Gateway to kick off the Lambda Function), my recommendation is to skip Lambda altogether and just go directly from APIGateway to DynamoDB. In your APIGW Integration Request, set it to use AWS Service instead of Lambda, then select DynamoDB as the service, Set the HTTP Method to POST, and set the Action to PutItem. And for the Integration Request Mapping Template, build out that same JSON as you are doing above {Item: {"id": {S: uuid}, "name": {S: name}}, TableName: "Users3"}.
I read that if we are using a Lambda Proxy Integration the post body have to be like this:
{
"body": "{\"name\":\"Mickael2\"}"
}
Depends what you mean by "post body", is that what you're sending or what you're receiving? If you're sending it, you can structure it however you want. If you're receiving it (i.e. in the lambda function), the structure will be as in your example, i.e. a body key in the event object, containing the stringified payload.
const body = JSON.parse(event.body).body;
If you're parsing the body, and the body is "{\"name\":\"Mickael2\"}" (i.e. stringified payload), then there is no body key/property on the resultant object, and therefore const body === undefined.
If you actually sent { body: "{\"name\":\"Mickael2\"}" } as the payload, which you shouldn't, then that code is valid.
const name = JSON.parse(body).name;
Depending on the actual payload, body is either an object, undefined, or a string at this point. If it's an object or undefined then parsing it again should throw an exception. If it's not doing so then perhaps it's because you are in fact supplying a payload of { body: "{\"name\":\"Mickael2\"}" }, in which case that code is valid, but as stated it's the wrong way to send the payload.
Your payload should be:
{
name: "Mickael2"
}
When it goes through proxy integration into your lambda, the event object will look like so (irrelevant bits removed):
{
body: "{\"name\":\"Mickael2\"}"
}
So this is how you should handle it in your lambda:
const payload = JSON.parse(event.body);
const name = payload.name;
Im trying to update a distribution list by sending a put request, when I run this code and test it in postman by giving it a JSON body I get this error in my node.js terminal stating SyntaxError: Unexpected end of JSON input ... any idea what I should change?
My PUT API request
app.put("/api/Dls/Add/:groupId" , (req, res) => {
const response = {
success: false
};
if (Authorized.myToken) {
response.success = true;
response.data = {};
var options = {
method: 'PUT',
url: 'https://SomeAPI.com/' + req.params.groupId,
headers:
{
Accept: 'application/json',
Authorization: 'Bearer' + ' ' + Authorized.myToken
},
body: JSON.stringify(req.body)
};
request(options, function (error, response, body){
if (error) {
console.log(error);
return;
}
const data = response.body;
const dls = JSON.parse(data)
return res.json(dls);
});
}
});
JSON body I'm passing through postman to test the API call
{
"groupId": "123456789",
"SomeField1": null,
"SomeField2": "xxxxxxxxx",
"SomeField3": true,
"SomeField4": "xxxxxxxxx",
"SomeField5": "xxxxxxxxx",
"SomeField6": [
"xxxxxxxxx"
],
"SomeField7": "xxxxxxxxx",
"SomeField8": "xxxxxxxxx",
"SomeField9": "xxxxxxxxx",
"SomeField10": "xxxxxxxxx",
"SomeField11": [],
"SomeField12": "xxxxxxxxx",
"SomeField13": null,
"SomeField14": false,
"SomeField15": ["xxxxxxxxx"]
}
Any feedback is appreciated!
If the JSON that you posted here is the real one that you pass via postman then, it is not the valid JSON as you have the same name properties. When I say valid it means you get something like this after posting to the endpoint.
{
"groupId": "123456789",
"SomeField": [
"xxxxxxxxx"
]
}
Request npm package is also deprecated so it is better to not use it and replace it with something like Axios. TBH I did not see any error in the code that causes the error that you mentioned, do you have access to the API to check the logs? Maybe something went wrong on the https://SomeAPI.com/ endpoint.
I figured out what the issue was, I needed to add the .end to the return statement
ex. return res.status(200).end()
I'm trying to use frisby.js to specify an API test for an endpoint that returns a plain text response to a valid POST request. However I'm having problems getting frysby.js to accept non-JSON response documents. Whenever a response returns non-JSON content, throw a TypeError due to 'Unexpected token b in JSON at position 0'.
As an example, I'm sending a HTTP POST request with the JSON document shown below is expected to return a response with a plaintext document with the string bar.
{
"foo":{
"name":"bar"
}
}
Here's the unit test that I've wrote to verify the response:
it('should create a foo resource', function () {
return frisby.post('http://localhost:8080/',
{
"foo":{
"name":"bar"
}
})
.expect('status',201);
});
Unfortunately, the following error is thrown by frisby.js when I run the test:
FAIL ./test.js
✕ should create a foo resource (17ms)
● should create a foo resource
TypeError: Invalid json response body: 'bar' at http://localhost:8080/ reason: 'Unexpected token b in JSON at position 0'
Does anyone know if it's possible to configure each test to expect some data format other than JSON?
If you getting JSON + something then break jsonTypes in two format like one for JSON object and one for other, like it having array in JSON objects.
and then put expect conditions on them.
This might helps you:
const frisby = require('frisby');
const Joi = frisby.Joi;
frisby.globalSetup({
headers : {
"Accept": "application/json",
"content-type" : "application/json",
}
});
it("should create a foo resource", function () {
frisby.post("http://localhost:8080/")
.expect("status", 200)
.expect("header", "content-type", "application/json; charset=utf-8")
.expect("jsonTypes", "data.foo", {
"name": Joi.string()
})
.then(function(res) { // res = FrisbyResponse object
var body = res.body;
body = JSON.parse(body);
expect(body.data.foo.name).toBeDefined();
})
});
This is the aws lambda function which will invoke an api:
'use strict';
var request = require("request")
exports.handler = function (event, context,callback) {
let url = "https://3sawt0jvzf.execute-api.us-east-1.amazonaws.com/prod/test"
request({
url: url,
method: "POST",
json: event,
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
callback(null, { "isBase64Encoded": true|false,
"statusCode": "200",
"headers": { "headerName": "headerValue"},
"body": body});
}
else {
console.log("error: " + error)
console.log("response.statusCode: " + response.statusCode)
console.log("response.statusText: " + response.statusText)
}
})
};
This is the api written as an aws lambda function:
'use strict';
exports.handler = function(event, context, callback) {
console.log(event.name);
callback(null, { "isBase64Encoded": true|false,
"statusCode": "200",
"headers": { "headerName": "headerValue"},
"body": `Hello World ${event.name}`}); // SUCCESS with message
};
When I try to call the api from the lambda function it just returns "Hello World undefined". It is not appending the name at the end and returning the correct response.
Assumptions:
You're using Lambda-Proxy Integration.
You want to pass the the exact same payload that the first Lambda received to the second Lambda.*
You're misunderstanding what event is. This is NOT the JSON payload that you sent through your HTTP request.
An HTTP request through the API Gateway gets transformed into an event object similar to this:
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {Incoming request headers}
"queryStringParameters": {query string parameters }
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext": {Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
As you can see, the JSON payload is accessible in a stringified form in event.body.
If you want to send the pass the same payload to the second Lambda, you have to parse it first.
const body = JSON.parse(event.body)
Then, send body instead of event.
Then, in your second Lambda, you parse the stringified JSON in event.body and then you get your original payload back.
If you sent name in that original payload, you can get it from JSON.parse(event.body).name.
Reference: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html#api-gateway-simple-proxy-for-lambda-input-format
Had a similar problem and debugged with logging event to console.
Add logging on the event,
console.log(JSON.stringify(event));
to evaluate how mapping is done in your API-Gateway to Lambda integration and see where the post parameter exists.
If the post value is not there fix the integration until you get the post values in your event.
Data Mapping API-Gateway to Lambda:
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
Hope it helps.
I had some very similar problem. When trying to call JSON.parse() on the event.body, I'm getting an error because event is already the object that I'm trying to POST. I haven't actually linked my database to my front end, but I have this error while testing from Lambda and from API Gateway.
Example of how I'm being able only now to read the object from my request:
"use strict";
const AWS = require("aws-sdk");
exports.handler = async (event, context) => {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody;
let statusCode;
console.log(event.id) // I have access to dot notation straight from the event
const {id, item, quantity, orderTotal, userId} = event;