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.
Related
I am working on a custom auth solution, I backend is on NODE (lambda) and calling that lambda by API gateway (it's a post-call).
For every thin is working fine if I use no-Proxy APIs, but in my case in need to pass custom additional headers. And when I tried with prox it is not responding. (it look like Cognito async call takes time or response is not resolving)
I am not sure I am missing some configuration or something wrong with code (code is working fine individual lambda and with API without Proxy).
here is my Lambda code.
// const AWS = require("aws-sdk");
// var crypto = require("crypto-js");
var CryptoJS = require("crypto-js");
var Crypto = require("crypto");
const { CognitoIdentityServiceProvider } = require("aws-sdk");
const hashSecret = (clientSecret, username, clientId) =>
Crypto.createHmac("SHA256", clientSecret)
.update(username + clientId)
.digest("base64");
async function isUserValid() {
const USER_POOL_ID = "poolid";
const CLIENT_ID = "clidntID";
const CLIENT_SECRET = "secreteCode";
const Password = "userName";
const Username = "password";
const cognito = new CognitoIdentityServiceProvider({
region: "ap-southeast-2",
});
try {
const payload = {
UserPoolId: USER_POOL_ID,
AuthFlow: "ADMIN_NO_SRP_AUTH",
ClientId: CLIENT_ID,
AuthParameters: {
USERNAME: "username",
PASSWORD: "password",
SECRET_HASH: hashSecret(CLIENT_SECRET, Username, CLIENT_ID),
},
};
const response = await cognito.adminInitiateAuth(payload).promise();
// // console.log("respone :", response)
// console.log("before response node js lambda ::: ")
return response;
} catch (e) {
console.log("error : ", e.message);
}
}
async function test() {
return "test ";
}
exports.handler = async (event) => {
// TODO implement
console.log("event : ", event);
'
const response = {
statusCode: 200,
headers: {
"Content-Type" : "application/json",
"Access-Control-Allow-Origin" : "*",
"X-Content-Type-Option" : "nosniff",
"Content-Security-Policy" : "default-src self",
"X-frame-options" : "DENY",
"Cache-Control" : "max-age=86400",
"X-XSS-protection" : 0,
"X-rate-limit": 600,
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
},
// body: await test() // (this response is working fine
body: await isUserValid(),
};
return response;
};
Errors when tested from API gateway with proxy setting on :
{
"message": "Internal server error"
}
Response Headers
{"x-amzn-ErrorType":"InternalServerErrorException"}
I tried multiple options but nothing is working for me.
The 'body' property must be a JSON string.
await cognito.adminInitiateAuth(payload).promise() is returning an object. You need to JSON.stringify() that response. Be sure to take care of the error case , too.
I made one todo app by using lambda. I used sever-less framework for deployment. I made one post request where user can create a todo-list. For testing I am using post-man. For my Lambda function I am using async function. I can able to make post request and my item store in Dynamo db but I got response Internal server error. My goal is show in my postman what I kind of post request I made.
Here is my code:
'use strict'
const AWS = require('aws-sdk');
const uuid = require('uuid');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.createTodo = async (event) => {
const datetime = new Date().toISOString();
const data = JSON.parse(event.body);
const params = {
TableName: 'todos',
Item: {
id: uuid.v1(),
task: data.task,
done: false,
createdAt: datetime,
updatedAt: datetime
}
};
try {
let data = await dynamoDb.put(params).promise();
console.log(data);
return JSON.stringify(data); // this throw me internal server error.
} catch (error) {
console.log(error);
}
};
Since you are calling the Lambda function via API Gateway, you need to convert the response to the following structure -
return {
statusCode: 200,
body: JSON.stringify(data),
// headers: {'Content-Type': 'application/json'}, // Uncomment if needed by your client
}
Goal: Get real-time stock data from IEX API, store it in DynamoDB, use data in DynamoDB to display on site.
Edit: I want to use the pull mechanism from the API where I can pull data periodically.
Currently, on API gateway, I created a get method for my stocks resource where I set the integration type to HTTP and entered the IEX Endpoint URL as the Endpoint URL. For now, I just got the data for one stock and the JSON Response Body looks like:
{
"symbol": "AAPL",
"companyName": "Apple Inc",
"primaryExchange": "NASDAQ/NGS (GLOBAL SELECT MARKET)",
"calculationPrice": "close",
"open": 132.5,
"openTime": 1610116201607,
"openSource": "official",
"close": 132.05,
"closeTime": 1610139600449,
"closeSource": "official"
}
I want to integrate Lambda here somehow so that it can get this API data and store it in DynamoDB. So far this is what my Lambda function looks like:
'use strict';
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody = "";
let statusCode = 0;
const { symbol, companyName } = JSON.parse(event.body);
const params = {
TableName: "Stocks",
Item: {
symbol: symbol,
companyName: companyName
}
};
try {
const data = await documentClient.put(params).promise();
responseBody = JSON.stringify(data);
statusCode = 201;
} catch(err) {
responseBody = `Unable to put product: ${err}`;
statusCode = 403;
}
const response = {
statusCode: statusCode,
headers: {
"Content-Type": "application/json"
},
body: responseBody
};
return response
};
How do I get the API data from API Gateway and integrate it with a Lambda function that stores this data to DynamoDB?
I am using the aws-sdk to get a pre-signed url for S3. I have the function wrapped in a lambda.
const aws = require('aws-sdk');
module.exports = CreateRecord => {
CreateRecord.controllers.createSignature = (event, context, callback) => {
const s3 = new aws.S3({
signatureVersion: 'v4',
});
const params = {
Bucket: 'random-test-bucket002',
Key: 'test-key',
Expires: 100
};
s3.getSignedUrl('putObject', params, function(err, signedUrl) {
let response;
if (err) {
response = {
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
error: 'Did not receive signed url'
}),
};
} else {
response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
},
body: JSON.stringify({
message: `Url successfully created`,
signedUrl,
})
};
}
callback(null, response);
});
};
};
This code works perfectly fine and I get back my pre-signed url. When I run this code on my front end:
postImage(uuid) {
const getSignature = 'https://xyz.execute-api.us-east-1.amazonaws.com/dev/v1/createSignature';
axios.get(getSignature)
.then(res => {
const signatureUrl = res.data.signedUrl;
// I have a blob that I store in file
// uuid is passed from another function
const file = new File([this.state.cover], uuid);
axios.post(signatureUrl, file)
.then(s3Res => {
console.log(s3Res);
});
});
}
The error I keep getting is: The request signature we calculated does not match the signature you provided. Check your key and signing method. I tried messing around with a few content-type headers but that did nothing. Can I pass the pre-signed url to a function in the aws-sdk? I've looked at a lot of posts on this but can't seem to resolve the issue.
When using pre-signed PutObject URLs for uploads to S3, you should upload files using the HTTP PUT method, rather than the HTTP POST method. You can POST objects to S3 but that's designed for browser-based uploads.
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),
})