call an external api using apigateway - amazon-web-services

I'm writing a lambda in node.js that will call an api(post) and gives back the resulting body and the code is as below.
const AWS = require('aws-sdk');
const request = require('request');
exports.handle = function(e, ctx, callback) {
var bodyDetails = {
uri: "myURL",
json: {
"requestChannel": "web1" },
"method": "POST"
};
callback = ctx.done;
var data = e.bodyJson || {};
request(bodyDetails, function(error, response, body) {
if (!error && response.statusCode === 200) {
console.log(JSON.parse(JSON.stringify(body)));
jsonBody = JSON.parse(JSON.stringify(body));
console.log(body + "\t from suvccess") // Print the json response
callback(null, jsonBody); // Return the JSON object back to our API call
} else {
callback(error);
}
});
}
and I'm testing the same in my lambda console. by passing a blank json {} and I get back the correct response.
Now my next plan is to integrate this piece against API Gateway. So I've created an api for this in my apigateway and in that, I've created a resource named home. and in the home, I created a GET method. with the below details.
Integration type: Lambda Function
Use Lambda Proxy integration : checked
Lambda Region: us-east-1
Lambda Function: myWorkingLambdaName
when I tested this using the test option given by apigateway. I get the response as
Request: /home
Status: 502
Latency: 2942 ms
Response Body
{
"message": "Internal server error"
}
when I see my console I see the values of the success block printed, but the status code is 502. This is very confusing, please let me know where am I going wrong and how can I fix this.
Thanks

API Gateway expects the following properties to be returned from your Lambda:
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
So, instead of callback(null, jsonBody), you should be calling callback like this:
callback(null, {
isBase64Encoded: false,
statusCode: 200,
headers: {
"Access-Control-Allow-Origin" : "*",
},
body: JSON.stringify(jsonBody),
})

Related

Serverless set cookie for AWS Api Gateway

It's my first question on Stackoverflow, normally I always found a solution here before Asking but not this time 😄
I use integration lambda mode, not the proxy mode.
I am able to set a cookie from serverless but I don't want to have the cookie in the body of my response.
I do this in the serverless.yml to set the cookie:
login:
handler: functions/auth/login.handler
events:
- http:
path: login
method: post
cors: true
integration: lambda
response:
headers:
Set-Cookie: integration.response.body.body
statusCodes:
200:
pattern: '' # Default response method
400:
pattern: '.*"statusCode": 400,.*'
template: $input.path("$.errorMessage")
401:
pattern: '.*"statusCode": 401,.*'
template: $input.path("$.errorMessage")
500:
pattern: '.*"statusCode": 500,.*'
template: $input.path("$.errorMessage")
here is the code of my lambda function:
/* login and return the jwt token infos and set the auth cookie */
import { ok, internalServerError, unAuthorized } from '../../lib/response.js';
import { login } from '../../lib/token.js';
import { logger } from '../../lib/logger.js';
import { authLambda } from '../../constants/constant.js';
export const handler = async (event) => {
logger.info('START', authLambda.login);
let responseObject;
let date = new Date();
date.setTime(+ date + (86400000)); // 24h
try {
const token = await login(event.body);
if (token) {
let cookie = 'auth='+token
responseObject = ok(cookie);
} else {
responseObject = unAuthorized();
}
logger.info('END => ', authLambda.login, responseObject);
return responseObject;
} catch (e) {
logger.error('ERROR', authLambda.login, e);
return new Error(internalServerError(e));
}
};
So, I have this as the response of the request:
{
"message": "Ok",
"statusCode": 200,
"body": "auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImJpbmN6YWttYXJ0aW40QGdtYWlsLmNvbSIsImlkIjoiOTUwNzI1YjAtODJlMy00Nzc0LWIwZDEtMWMxMTcyM2UzMTY3Iiwicm9sZSI6IkVuZ2luZWVyIiwiaWF0IjoxNjcwNDk0NjYzLCJleHAiOjE2NzA1ODEwNjN9.Y7X4AiVV84rCFLvuGTHbcyKMfZhpZuq3iWERzkHRZS0"
}
But I only want to have this:
{
"message": "Ok",
"statusCode": 200
}
Thanks 🙂

Lambda authorizer receive event.authorizationToken undefined even tought identity source is set

I have a websocket API with this authorizer:
image
on the $connect route:
image
IN the authorizer, after logging the received event:
export async function handle(
event: CustomAuthorizerEvent
): Promise<AuthResponse> {
try {
console.log(JSON.stringify(event));
if (!event.authorizationToken) {
throw new Error("authorization token not found");
}
// ......
but event.authorizationToken is not define, even though event.headers.Authorization is set as we can see here:
{
"type": "REQUEST",
"methodArn": "string",
"headers": {
"Authorization": "The token I sent", <---------- here
..............
}
Why is this happening, and how can I fix it???

AWS Lambda Function : {"message":"Internal Server Error"

Im trying to run the United States Postal Service's Web Tools, for converting ZIP Codes into State and City. I created an AWS Lambda function inside the AWS Amplify.
But the Lambda function is always giving me the return message {"message":"Internal Server Error"}
Here is my Lambda FUnction Code.
const axios = require("axios");
const BASE_URI =
"http://production.shippingapis.com/ShippingAPITest.dll?API=CityStateLookup&XML=";
const config = {
headers: {
"Content-Type": "text/xml",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Methods": "GET",
},
method: "get",
};
exports.handler = async function (event, context, callback) {
// The zipcode is sent by the frontend application.
// This is where we use it.
const zipcode = event.queryStringParameters.zipcode;
// The xml variable is the string we are going to send to the
// USPS to request the information
const xml = `<CityStateLookupRequest USERID="400000000"><ZipCode ID="0"><Zip5>${zipcode}</Zip5></ZipCode></CityStateLookupRequest>`;
try {
// Using syntactic sugar (async/await) we send a fetch request
// with all the required information to the USPS.
const response = await axios(`${BASE_URI}${xml}`, config);
// We first check if we got a good response. response.ok is
// saying "hey backend API, did we receive a good response?"
if (!response.ok) {
// If we did get a good response we store the response
// object in the variable
return { statusCode: response.status, body: response };
}
// Format the response as text because the USPS response is
// not JSON but XML
const data = await response.text();
// Return the response to the frontend where it will be used.
return {
statusCode: 200,
body: data,
};
// Error checking is very important because if we don't get a
// response this is what we will use to troubleshoot problems
} catch (err) {
console.log("Error: ", err);
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }),
};
}
};
The axios is working fine I think.
Any help would be appreciated as I'm trying to solve this for days now.
By default Lambda functions does not have outbound access to internet.
You can add a Nat Gateway to your VPC but it's not cheap.

Not receiving SMS when sending it through SNS in lambda

I am sending the OTP using AWS SNS service when in a lambda function my code is this
const otp = generateOTP();
const message = `Your XXXX Verification Code is ${otp} .`;
var params = {
Message: message,
PhoneNumber: "+91xxxxxxxxxx",
};
AWS.config.update({region: 'ap-south-1'});
var publishTextPromise = new AWS.SNS({apiVersion: '2022-04-01'}).publish(params).promise();
return new Promise((resolve, reject) => {
publishTextPromise.then((data) => {
console.log("MessageID is " + data.MessageId);
const response = {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
};
console.log('Server response function');
resolve(response);
}).catch((error) => { reject(Error(error)); });
});
and i am getting this response when invoking this lambda function
Server response function
{
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"ResponseMetadata": {
"RequestId": "29796d67-f32e-5e42-a761-37a72809164d"
},
"MessageId": "2f8ffee0-7e0d-5cd3-a5b0-a420a52a14dc"
}
}
But I am not receiving the SMS on that number I don't know what is the problem here cause the I am getting resolved response in the console which means the SMS is send but I am not received it.
Do I have to config the sns manual in aws sns mobile -> text messaging or something??
or is their any other way to send verification otp ?

Express Gateway: Not getting response body

I am not being able to get the response body that I need to store in the logs along with the request body. The only time I'm able to get the response body is when the request fails.
I've followed the blog post that solved my issue on getting the request body while using body-parser plugin - https://www.express-gateway.io/exploit-request-stream/.
const { PassThrough } = require("stream");
const jsonParser = require("express").json();
const urlEncodedParser = require("express").urlencoded({ extended: true });
module.exports = {
name: 'body-parser',
policy: actionParams => {
return (req, res, next) => {
req.egContext.requestStream = new PassThrough()
req.pipe(req.egContext.requestStream)
return jsonParser(req, res, () => urlEncodedParser(req, res, next))
}
}
};
When the request does work:
{ res: { statusCode: 400 },
req:
{ body: { a: 'b' },
headers:
{ ... } },
responseTime: 310 }
When it does not work:
{ res: { body: 'Bad gateway.', statusCode: 502 },
req:
{ body: { a: 'b' },
headers:
{ ... } },
responseTime: 1019 }
this code alone is not enough to get the response body. This will simply hook in the request body processing and make it available to EG in a parsed way. In case you want to hook in the response too, you will need to write an hook in the response object, once it's done.
You can find an example code here
I hope that helps!
V.