Serverless invoke works but curl gives error - aws-lambda

I have a serverless function, you can find the code below, and this function is deployed on aws lambda using the serverless framework. from within this function i call an external web api.
When i do serverless invoke -f my_func i get the expected response. but if i run a curl command it fails and i get {"message": "Internal server error"}
this is my curl command:
curl -X GET \
https://0wb3echzu8.execute-api.us-east-1.amazonaws.com/dev/my_func \
-H 'cache-control: no-cache' \
-H 'postman-token: 1fc551c0-7010-e1e3-7287-6a0d82d1e91a'
this is my code:
var request = require("request");
var options = { method: 'GET',
url: 'https://api.coinmarketcap.com/v2/listings/',
headers:
{ 'postman-token': '090e284e-62ad-29f0-0f10-92ae14656a37',
'cache-control': 'no-cache' } };
module.exports.my_func = (event, context, callback) => {
request(options, function (error, response, body) {
if (error) { console.log(error); callback(null, error) }
console.log(body)
callback(null, body)
});
};
this is the serverless.yml file:
service: aws-nodejs
app: sonarsic
tenant: vincent
provider:
name: aws
runtime: nodejs6.10
functions:
my_func:
handler: handler.my_func
events:
- http:
path: my_func
method: get
cors: true
It must have to do something with the calling of the web api in my function. if i don't call a web api it works fine.
If I check the logs via serverless logs -f my_func i only get the logs of the calls that worked using serverless invoke.
what can i do to find out what is going wrong inside my function when making a curl command?
adding cors:true to the http section does not solve the problem
cloudwatch is attached to aws lambda but it seems there is nothing written to:

After some discussion on chat, we discovered that statusCode on the response body is missing
let request = require('request');
let options = {
method: 'GET',
url: 'https://api.coinmarketcap.com/v2/listings/',
headers: {
'postman-token': '090e284e-62ad-29f0-0f10-92ae14656a37',
'cache-control': 'no-cache',
},
};
module.exports.my_func = (event, context, callback) => {
request(options, (error, response, body) => {
if (error) { console.log(error); callback(null, error) }
console.log(body)
callback(null, { body, statusCode: 200 }) // response statusCode added
});
};

Related

NodeJS: AWS SDK V3: Not receiving any response data from lambda function

I'm trying to use the v3 javascript sdk to invoke a AWS Lambda function, and I'm having problems getting any meaningful response.
My code looks like so...
const { Lambda } = require("#aws-sdk/client-lambda");
const client = new Lambda();
const params = {
FunctionName: "MyLamdaFuncton",
Payload: JSON.stringify({ "action": "do_something" }),
InvocationType: "Event"
};
client.invoke(params)
.then((response) => {
console.log(JSON.stringify(response,null,4));
})
.catch((err) => {
console.error(err);
})
I can confirm from checking the CloudWatch logs that the lambda function works as exepcted. However this is the response I get in my NodeJS code...
{
"$metadata": {
"httpStatusCode": 202,
"requestId": "d6ba189d-9156-4f01-bd51-efe34a66fe34",
"attempts": 1,
"totalRetryDelay": 0
},
"Payload": {}
}
How do I get the actual response and status from the Lambda function?
If I change the payload above to intentionally throw an exception in my Lambda, the response in the console is still exactly the same.
update:
The Lambda function is written in Ruby. The response is returned like so...
{ statusCode: 200, body: JSON.generate(response.success?) }
where "response" is from another service it calls internally.
I've figured out what I was doing wrong. The issue was the "InvocationType". I got it working by changing to...
InvocationType: "RequestResponse"
Then I had to extract the response data like so...
const response_data = JSON.parse(new TextDecoder("utf-8").decode(response.Payload))

Sam not encoding request body while being locally invoked

The event body is not getting encoded while invoking sam local start-api and sending a multipart request but it's being encoded in the cloud. So, I'd like to have the same behavior in my local environment.
Steps to reproduce the issue:
Create the Hello World project provided by sam init
Add a post method
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: post
ContentHandling: CONVERT_TO_BINARY # This is not doing the magic as I was expecting
BinaryMediaTypes:
- "*/*"
- multipart/form-data
Return the isBase64Encoded in the handler.
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
'statusCode': 200,
'body': JSON.stringify({
message: event.isBase64Encoded,
// location: ret.data.trim()
})
}
} catch (err) {
console.log(err);
return err;
}
return response
};
Perform the HTTP request:
curl --location --request POST 'http://127.0.0.1:3000/hello' \
--header 'saa: csv/csv-file' \
--form 'foo=#/home/user/csv-file.csv'
The response is always the same:
{
"message": false
}
I've tried to use the proxy integration but it didn't work.
My workaround is to have something like this in the handler:
const csvString = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('utf-8') : event.body;

How to call one lambda from another in AWS SAM

I'm writing application with multiple functions inside SAM application. I can invoke lambda function that is already deployed to AWS with code similar to AWS Lambda call Lambda but it doens't work with local functions. I tried things from https://github.com/awslabs/aws-sam-cli/issues/510 but nothing seem to work yet.
This is closest I got yet (you need sam local start-lambda --host 172.17.0.1 where host is in docker network)
var AWS = require("aws-sdk");
exports.lambdaHandler = async (event, context) => {
let lambda = new AWS.Lambda({});
if (process.env.AWS_SAM_LOCAL) {
var ep = new AWS.Endpoint("http://172.17.0.1:3001");
lambda = new AWS.Lambda({ endpoint: ep });
}
const body = await new Promise(r => {
lambda.invokeAsync(
{
FunctionName: "myFunction",
InvokeArgs: JSON.stringify({ arguments: "for other function" })
},
function(err, data) {
if (err) console.log(err, err.stack);
else console.log(data);
r(data);
}
);
});
response = {
statusCode: 200,
body: JSON.stringify(body)
};
return response;
};
and at least I see some activity, but I get error in invokeAsync
PathNotFoundLocally: PathNotFoundException\n at Object.extractError (/var/task/node_modules/aws-sdk/lib/protocol/json.js:51:27)\n
and this error in start-lambda console
2019-12-20 16:07:02 172.17.0.7 - - [20/Dec/2019 16:07:02] "POST /2014-11-13/functions/myFunction/invoke-async/ HTTP/1.1" 404 -
UPDATE:
I was able to fix ssl error that I had in first version of this question, and it looks like it works in sync mode with lambda.invoke
It looks like this is not currently supported until https://github.com/awslabs/aws-sam-cli/pull/749 is merged

trying to fetch on aws lambda using serverless

I'm trying to run a simple script on AWS Lambda using Serverless to push it, the script fetches a url and returns it (a proxy), for some reason I can't see the response.
The script in question:
'use strict';
let axios = require('axios')
module.exports.hello = async (event, context) => {
let res = await axios.get('http://example.com')
return {
statusCode: 200,
body: JSON.stringify({
message: res,
input: event,
}),
}
};
My serverless YML:
service: get-soundcloud-tracks
provider:
name: aws
runtime: nodejs8.10
profile: home
functions:
hello:
handler: handler.hello
events:
- http:
path: users/create
method: get
cors: true
The solution was changing res to res.data inside the JSON.stringify

API Gateway + Lambda - CORS Issue

i am experiencing continuing problems with the CORS integration for API Gateway + Lambda. i have enabled CORs for the resources associated with the API. Everything appears to work fine via Lambda testing, Postman testing etc, but calling the api from a webpage script is giving the following error "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 415." Do I need to change the Lambda function? Thanks
Here is my simple Lambda code..
'use strict';
var AWS = require('aws-sdk');
var dclient = new AWS.DynamoDB.DocumentClient();
var getItems = (event, context, callback) => {
var params = {
TableName: "OMSCaseDataTest",
Key: {
"IncidentID": event.IncidentID
}
}
dclient.get(params, (error, data) => {
if (error) {
callback(null, "error occured")
} else {
callback(null, data);
}
});
};
exports.getItems = getItems;
If you are using proxy integration in API Gateway, then enabling CORS from API Gateway doesn't work. You have to set the Header 'Access-Control-Allow-Origin' from your Lambda code itself.
Its mentioned in the doc.
Python code sample:
response = {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps({'message': 'CORS enabled')
}
return response
Assuming you're using proxy integration, you'll need to handle the CORS yourself. Your lambda function will need to handle the HTTP methods differently. CORS problems usually occur when the pre-flight option request is not entertained. Here's a code snippet could help your cause.
function main(event, context, lambdaCallback) {
if (event.httpMethod === 'OPTIONS') {
doneOptions(200, '{"status": "OK"}', 'application/json', lambdaCallback);
} else if (event.httpMethod === 'POST') {
// do your POST here
} else {
return done(400, '{"message":"Invalid HTTP Method"}', 'application/json', lambdaCallback);
}
}
The functions that return the HTTP 200 to your frontend which decide what your frontend/API could call and what's not.
function doneOptions(statusCode, body, contentType, lambdaCallback, isBase64Encoded = false) {
lambdaCallback(null, {
statusCode: statusCode,
isBase64Encoded: isBase64Encoded,
body: body,
headers: {
'Content-Type': contentType,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Authorization,Content-Type',
'Access-Control-Allow-Method': 'GET,POST,OPTIONS',
}
});
}
Some times the lambda time out causes CORS error. You can increase the timeout this may fix the issue.

Resources