Get PDF from API using AWS API gateway and Lambda - amazon-web-services

I have an internal API that provides data in different formats by just passing the id + format. For Example if I want to get a PDF of a produc with ID = 1 I will just make a call to the app with apiurl/latest/1.pdf.
This works fine when I am in the internal network since the host is only available internally. To access it publicly, we have implemented an authorization using API gateway and Lambda. Lambda takes are of the authorization and return the result just fine :
When I request JSON data
When I request XML data
Here a sample version of the lambda:
var request = require('request');
exports.handler = function(event, context, callback) {
var fUrl = event.fUrl + event.pid;
if(event.fsUrl.indexOf('product') >-1){
fUrl = fUrl + '.' + event.format
}
request({
url: fUrl,
}, function(error, response, body) {
if(error){
return callback(error);
}else{
return callback(null, response.body);
}
});
}
but not PDF. Some screens from the postman. I used both Send and Download in Postman.
Any thoughts?

Related

How to create a NodeJS Authorization middleware on a serverless framework?

I'd like to create a middleware that checks the authorization header, decodes the token and sends the decoded data to the actual function, just like you would by adding userData to the request and using next() on an Express server, so the actual function gets back the decoded data on the req and it can then check what content to display to the user (if any).
I'm using Lambda functions on a serverless framework.
This was the function on my Express NodeJS local server:
const authorizerFunc = async (req, res, next) => {
let token;
try {
if (
req.headers.authorization &&
req.headers.authorization.split(" ")[0] === "Bearer"
) {
token = req.headers.authorization.split(" ")[1];
}
if (!token) {
req.userData = { userId: "", username: "" };
next();
return;
}
const decodedToken = jwt.verify(token, process.env.JWT_SECRET_KEY);
console.log("DECODED TOKEN", decodedToken);
req.userData = {
userId: decodedToken.userId,
username: decodedToken.username,
email: decodedToken.email,
};
next();
} catch (err) {
req.userData = { userId: "", username: "" };
next();
return;
}
};
The question is, how do I create a Lambda function that does this and sends the decoded data to the real function?
Edit: is it bad if I decode the auth token directly in the functions at the very beginning? I don't think it would add huge complexity to them.
Well, I don't have an actuall example for the serverless framework, but i can tell what you should do.
Create an Lambda Function to act as a Amazon API Gateway Lambda authorizer - you can see the documentation here - https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
make sure you do the validation logic what you have defined, and also return the context object in the response - which you can define your user data
add the Amazon API Gateway Lambda authorizer to the API Gateway - https://docs.aws.amazon.com/apigateway/latest/developerguide/configure-api-gateway-lambda-authorization-with-console.html
If the authorization successful your rest api lambda can access the context object with the user data, which you customize in step 2

How to write data from an external API to DynamoDB?

I want to get real-time data from a stock API (IEX API) and load it to DynamoDB. Most of the tutorials I've watched so far (like this around 15:10) show how to create Lambda functions and integrate with API gateway. However, they are still manually entering the data to load to the table. How do I use the API Gateway and Lambda to get data from the IEX API and load it to DynamoDB rather than writing the data myself?
Following some tutorials, my Lambda function for loading data to the table is:
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody = "";
let statusCode = 0;
const { id, stocksymbol} = JSON.parse(event.body);
const params = {
TableName: "StockData",
Item: {
id: id,
stocksymbol: stocksymbol
}
};
try {
const data = await documentClient.put(params).promise();
responseBody = JSON.stringify(data);
statusCode = 201;
} catch(err) {
responseBody = `Unable to put item: ${err}`;
statusCode = 403;
}
const response = {
statusCode: statusCode,
headers: {
"Content-Type": "application/json"
},
body: responseBody
};
return response
};
I would be getting more data from the API (opening price, closing price etc.) but this is what I have for now.
Assuming you need to utilize pull mechanism (you need to get data from API yourself), you can use AWS EventBridge rule to trigger your lambda periodically with interval of your preference - https://docs.aws.amazon.com/eventbridge/latest/userguide/create-eventbridge-scheduled-rule.html. In lambda you download the API data and store them in DynamoDb.
If you can use push mechanism (you can get data pushed to you, e.g. https://iexcloud.io/docs/api/?gclid=CjwKCAiA_9r_BRBZEiwAHZ_v17o9kJuPyF5Do_E3_mwC0uHEh2yXqqOdtVgqvc34yEk5RR8W8028HRoC0HUQAvD_BwE#webhooks), you can set the your API gateway resource URL as a target path, while having your lambda function as the handler for the API Gateway resource URL, storing the pushed data to the DynamoDb.

How do you pass query string parameters to the AWS API Gateway client with the Javascript SDK?

I have an endpoint defined in AWS API Gateway that uses a Lambda integration. The Lambda function expects query string parameters that would be available in the event object passed to it.
My API is at example.execute-api.us-east-1.amazonaws.com/dev/my-resource and I have query string parameters like foo=test.
So the full endpoint would be
example.execute-api.us-east-1.amazonaws.com/dev/my-resource?foo=test
I can visit this endpoint in a browser or request it in postman, and get the expected response, so I know that the API Gateway is configured properly. However when I use the Javascript SDK, I can't seem to pass query string parameters.
According to the last part of this page from the docs, I should be able to just pass in a JSON object that will be interpreted as query string parameters, like so:
var apiClient = apigClientFactory.newClient();
var requestParams = {"foo": "test"};
apiClient.myResourceGet(requestParams).then(function(result) {
// Do something with the response
});
However, in my case requestParams seems to be ignored. In the Lambda function, the event has an empty queryStringParameters field. How can I pass the key/values defined in the requestParams object as query string parameters to this endpoint?
since your following end point passing query param , you really no need json objet
example.execute-api.us-east-1.amazonaws.com/dev/my-resource?foo=test
create variable
var test = <assign value>
now
var params = {
host: "execute-api.us-east-1.amazonaws.com",
path: "/dev/my-resource?foo="+test
};
Example :
var https = require('https');
exports.handler = (event, context, callback) => {
var params = {
host: "bittrex.com",
path: "/api/v1.1/public/getmarketsummaries"
};
var req = https.request(params, function(res) {
let data = '';
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
console.log("DONE");
console.log(JSON.parse(data));
});
});
req.end();
};

How to get current user username in AWS Lambda?

I use AWS Lambda + Cognito (User Pool + Federated Identity) + API Gateway. Users authenticate in WEB application with amazon-cognito-identity-js and invokes API with aws-api-gateway-client. API Gateway methods have AWS_IAM authorizer. How to get username (from User Pool) in Lambda function?
You can use event.identity.username
exports.handler = async (event, _, callback) => {
try {
console.log('event', event);
console.log('event.identity.username', event.identity.username);
const userId = event.identity.username;
console.log('userId', userId);
callback(null, true);
} catch(e) {
console.log(e);
callback(e);
}
};
Modify the request sent to your Lambda function using aws-api-gateway-client to pass the JWT ID Token in the request header.
You may need to ensure your API gateway is configured to forward headers.
apigClient.invokeApi(
params,
pathTemplate,
method,
{ { headers: { IDToken } } },
body);
The ID Token should be used here as its payload contains cognito:username field
The ID Token is gotten after authentication using amazon-cognito-identity-js.
You can parse this field from the header of the request in your lambda handler function.
Verify its signature before trusting the contents of its payload.
import { util } from 'aws-sdk/global';
exports.handler = function(event, context) {
// Parse ID Token from request header
const headers = event.headers;
const idToken = headers.IDToken;
...
};

How do I respond with a raw buffer instead of toString in Amazon API Gateway - Lambda

Here is my code thus far - I'd like to just return the image buffer as raw data rather than having it toString to an array.
I set the content-type to image/jpeg in the integration response on a http 200 response, but it's a broken image because I think it's a toString of the buffer rather than the raw data.
exports.handler = function(event, context) {
var srcKey = event.key || 'e_1.png';
var max_size = event.size || 100;
// Download the image from S3
s3.getObject({
Bucket: srcBucket,
Key: srcKey
}, function (err, response) {
if (err)
return context.fail('unable to download image ' + err);
var original = gm(response.Body);
original.size(function (err, size) {
if (err)
return context.fail('unable to download image ' + err);
resize_photo(size, max_size, original, function (err, photo) {
//res.setHeader('Content-Type', 'image/jpeg');
context.succeed(photo);
});
});
});
};
This doesn't seem like something Lambda with API Gateway was designed for. Binary output may not be supported given the state of it's pipeline. Try doing something else instead - store the image back to the S3 and send back a HTTP Redirect to new S3 URI. Let the client handle it instead of trying to make the API Gateway pipeline handle binary responses.