should I create Public api based on the current internal api - amazon-web-services

My project is having public website and Content Management System(CMS).
I am using Lambda and API Gateway for the api.
The CMS currently has an api GET request to get ALL the data from the table below.
“Banner” Table
attribute:
-id: string(primary key/partition key)
-title: string
-isActive: boolean
...
-----------------------------------------------
Id isActive title
1 true title1
2 false title2
3 true title3
4 true title4
----------------------------------------------
This is My lambda function:
getBanner.js
'use strict'
const AWS = require('aws-sdk');
exports.handler = async function (event, context, callback) {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody = "";
let statusCode = 0;
const params = {
TableName : "Banner",
};
try{
const data = await documentClient.scan(params).promise();
responseBody = JSON.stringify(data.Items);
statusCode = 200
}catch(err){
statusCode = 403
}
const response = {
statusCode: statusCode,
body: responseBody
}
return response
}
I need an api to get all banners with isActive = true.
There are 2 approaches I can think of
1.Modify the existing lambda function
I can add something like below:
…
if(event.queryStringParameters.isActive === true){
// add filter or query to get all result
}
...
But everyone is able to get all the data(including the results with isActive = false) if they do not use the queryStringParameters, which is what I want to avoid since the data with isActive = false should not be seen by the public.
2.Create new lambda function
This is probably the best way to protect the data.
But I have a lot of API encountering the same situation(having “inActive” attribute), it means I need to create a lot of public api.
Which method should I use?

You could create a public API Gateway that points to the same Lambda functions, and in the API Gateway configuration have it always pass the isActive=true parameter so that users of the public API wouldn't be able to override that.

Related

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 to do dynamodb pagination in lambda function with nodejs

I want the frontend to decide the currentPage, pageSize(number of data in a page) and the sorting(+ mean ascending/- mean descending) in event.queryStringParameters.
Dynamodb attribute
-id(string)(partition key)
-createdAt(date)
-url(string)
-createdBy(string)
-title(string)
But I am troubled to create this function, I can only set the pageSize, and I don't know how to deal with currentPage and sorting in my function.
Below is my current lambda function.
getBanner.js
'use strict'
const AWS = require('aws-sdk');
exports.handler = async function (event, context, callback) {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody = "";
let statusCode = 0;
const queryParams = JSON.stringify(event.queryStringParameters);
/* queryParams will look like this
{
"currentPage": "0",
"pageSize": "30",
"sorting": "-status"
}
*/
const params = {
TableName : "Banner",
Limit:queryParams.pageSize
};
try{
const data = await documentClient.scan(params).promise();
responseBody = JSON.stringify(data.Items);
statusCode = 200
}catch(err){
responseBody = `Unable to get Alerts: ${err}`;
statusCode = 403
}
const response = {
statusCode: statusCode,
headers:{
"Content-Type": "application/json",
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
},
body: responseBody
}
return response
}
Pagination doesn't work that way in DynamoDB (link to docs). This is another example of where knowledge of SQL database practices can hinder your understanding of DynamoDB.
DynamoDB has a limit of 1MB responses for scan and query operations. Anything larger than that will be paginated. This means that DynamoDB will paginate results on the size of the response, not the number of items in the response. This limitation ensures that your responses stay small and manageable, while your queries remain performant.
Fetching 1MB of data at a time is different than fetching N rows at a time, as is common with pagination in SQL databases. Take some time to read up on how DDB handles pagination with the LastEvaluatedKey and ExclusiveStartKey before implementing a front-end solution.

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();
};

s3 list objects return null with sdk

I have a lambda function with has a role with Admin access. I have a S3 bucket and I want to test listing of its contents. Here is the code
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
exports.handler = async (event) => {
var params = { Bucket: 'test-bucket-mine' };
let result = await s3.listObjectsV2(params);
console.log(result);
};
However, the console log looks like the following.
Response:
null
I am testing using lambda test with basic event. Please let me know where I am doing it wrong.
You are awaiting the request object. The request is never being fired. Try:
let result = await s3.listObjectsV2(params).promise();
This will start the request and return a promise that resolves when the request completes. You will then await the result of that promise.

How do I get response headers for AWS JavaScript SDK calls?

I have a simple AWS Lambda function which makes an S3.getObject() call as follows:
const AWS = require('aws-sdk');
AWS.config.logger = console;
const s3 = new AWS.S3();
exports.handler = async (event) => {
return await getObject({
Bucket: "<MY-BUCKET>",
Key: "<MY-KEY>"
}).then( (res) => {
console.log('Retrieved object from S3');
console.log(res);
return res.Body.toString('ascii');
})
};
async function getObject(params){
return await s3.getObject(params).promise();
}
I've enabled logging SDK calls as per this document.
How do I get response headers for the s3.getObject() SDK call that was made? I am basically trying to retrieve the S3 request ID and extended request ID.
The in-built logger added via the "AWS.config.logger = console;" line does not seem to log response headers. How else do I get response headers for AWS JavaScript SDK calls?
P.S: Bonus points if you can let me know whether or not I need two await keywords in the code above.
Listen to httpHeaders event.
var requestObject = s3.getObject(params);
requestObject.on('httpHeaders', (statusCode, headers, response, statusMessage) => {
// your code here.
});
requestObject.promise()
.then(response => { ... })