Imagine you have a big box of Legos. With these Legos, you can build lots of different things, like houses, cars, and even spaceships. But to build these things, you need a place to keep your Legos and enough pieces to make whatever you want.
Now, think of Amazon Web Services as a huge, virtual box of Legos that lives on the internet. Instead of building houses or cars, people use AWS to build websites, store information, and do lots of other important computer work.
Just like you can share your Legos with friends, people can use AWS to share their creations with others over the internet. They can build big websites, store lots of pictures and videos, or even make new apps for phones and tablets.
The cool part is that you don’t need to have all the computer pieces at your home. AWS has a super big computer system that anyone can use over the internet. So, it’s like having a never-ending supply of Legos that you can use anytime you want to build something new and exciting!
AWS provides different services. Following are some of the example for the services provided by AWS:
- EC2 (Elastic Compute Cloud): Think of EC2 like renting a powerful computer on the internet. You can use this computer to run websites, applications, or any software you need. It’s like having a really good computer that you can use for a while, without actually buying it.
- S3 (Simple Storage Service): This is like a huge online storage locker. You can put all sorts of things in it like photos, videos, and documents. It’s always there when you need it, and you can store as much as you want and get it back whenever you need.
- RDS (Relational Database Service): Imagine a big, super-organized filing cabinet for all your data. RDS lets you store and manage lots of information (like customer details or product information) in a very organized way. It’s like having a librarian who keeps all your data neat and tidy.
- Lambda: This is a bit like having a magic genie. You tell Lambda a small task you want done, like resizing a photo when you upload it, and Lambda does it for you automatically. You don’t need to worry about how it gets done; Lambda takes care of it.
- CloudFront: Think of CloudFront as a super-fast delivery truck for your website. It makes sure that when people visit your website, they get everything they need (like pictures and videos) really quickly, no matter where in the world they are.
- IAM (Identity and Access Management): IAM is like the security guard for your AWS services. It helps you manage who is allowed to do what. Like, who can open your storage locker, or who can use your rented computer. It makes sure that only the people you trust can use your AWS stuff.
These are just a few of the many services AWS offers, but they’re some of the most popular and widely used. Each service is designed to make specific tasks easier and more efficient for businesses and developers.
In this article, I am going to build a Simple CRUD application using the AWS Services such as Dynamo DB, API Gateway & Lambda along with IAM Role.
Imagine you have a small library of children’s storybooks. You want to create a simple system where kids can check online to see if their favorite book is available. Here’s how the AWS services fit in:
- DynamoDB: This is like your digital catalog of books. Each book in your library is listed here with details like the title, author, and whether it’s currently available or borrowed.
- Lambda: Think of Lambda as your helpful librarian. When a kid wants to know if a book is available, Lambda checks the DynamoDB catalog for them. It looks up the book and tells them if it’s there or not.
- API Gateway: This is the library’s front desk. Kids come here (through a website or app) and ask, “Is ‘Where the Wild Things Are’ available right now?” The API Gateway takes this question and passes it to Lambda, the librarian, to get the answer.
- IAM Role: The IAM Role is like a set of rules for who can do what in the library. It makes sure that only the librarian (Lambda) can access the book catalog (DynamoDB) and that only the front desk (API Gateway) can talk to the librarian. This keeps everything secure and running smoothly.
So, when a kid asks if a book is available, their question goes to the API Gateway, which then asks Lambda. Lambda checks the DynamoDB catalog and then tells the kid if the book is available or not, all in just a few seconds. This makes it really easy for kids to find out if they can borrow their favorite storybook!
Now, let’s take this as a example and build a small CRUD Application.
Let’s first start with creating a Dynamo DB table.
Go to AWS Console and Search for Dynamo DB service,
Click on Tables in the Sidebar -> Click “Create table“
Under Table Name: type whatever table name you want to have
-> children_library
For Partition Key, it should be the Primary key of the table and it should be unique as well.
-> book_id
Keep other things as default values and click the Create table button.
This will take some time to create the DB.
Now, let’s move to creating a Lambda function, which will be act as a backend based on the API request it will retrieve or write data to our created Dynamo DB.
Search and click on “Lambda” service:
Click on “Functions” in the sidebar -> click “Create function“
Select -> Author from Scratch
Function name -> serverless-api
Runtime -> Node JS 16.x
In the Change default execution role
Check -> “Create a new role from AWS policy templates“
Role name -> type any meaning full name. -> serverless-api-role
Keep others as default and click “Create function“
This will take up a few seconds to create the function
Since we created a role for this, Let’s go to the IAM service and Assign Role Permission to access Dynamo DB & Cloud Watch Logs (Will do a seperate post on Cloud Watch later) and click “Roles” in sidebar.
Click on the role that you assigned for Lambda,
In the Permissions tab -> Permissions policies -> click “Add permissions” -> Attach policies ->
Search and apply the following Policies
- AmazonDynamoDBFullAccess
- CloudWatchLogsFullAccess
Why to add these policies to this role:
- AmazonDynamoDBFullAccess: This policy provides full access to Amazon DynamoDB. When you assign this policy to a Lambda function, it grants the function permissions to perform all actions on DynamoDB. This includes creating, reading, updating, and deleting data in DynamoDB tables. This is crucial if your Lambda function needs to interact with a DynamoDB database, such as retrieving data for processing, storing results, or maintaining a database.
- CloudWatchLogsFullAccess: This policy grants full access to Amazon CloudWatch Logs. CloudWatch is a monitoring and observability service. By assigning this policy to a Lambda function, you enable the function to create and manage log groups and log streams in Amazon CloudWatch Logs. This is essential for logging the operation of your Lambda function, which helps in monitoring its performance, debugging issues, and keeping records of its activity. Effective logging is a key aspect of understanding and maintaining the health of applications running on AWS.
Now lets create API Endpoints in API Gateway.
Search for “API Gateway“,
Click on APIs in sidebar:
Click “Create API“
A new page will be shown up with “Choose an API type“:
Following types are available at the time of writing this article:
- HTTP API
- WebSocket API
- REST API
- REST API Private
Let’s walk through a small level of understanding about these type of API:
- HTTP API:
- Purpose: HTTP APIs in AWS are primarily used for building scalable and cost-effective RESTful APIs.
- Key Features:
- Designed for low-latency, cost-effective integrations.
- Support for OAuth 2.0 and JWT (JSON Web Token) authorization.
- Ideal for serverless workloads using AWS Lambda.
- Simplified routing and payload formatting.
- Use Case: Best suited for simple, HTTP-based backend services where routing, authorization, and scalability are important.
- WebSocket API:
- Purpose: WebSocket APIs are used to create real-time, two-way communication applications.
- Key Features:
- Enables server to push real-time updates to clients.
- Maintains a persistent connection between client and server.
- Supports messaging or chat applications, real-time notifications, and more.
- Use Case: Ideal for applications that require real-time data updates, such as chat applications, real-time gaming, or live notifications.
- REST API:
- Purpose: REST APIs (Representational State Transfer) in AWS are for creating fully-featured RESTful APIs. They offer more features compared to HTTP APIs
- Key Features:
- Supports RESTful interface and can work with JSON, XML, or other formats.
- Advanced features like request validation, request and response transformations, and more.
- Detailed monitoring and logging capabilities.
- Use Case: Suitable for APIs that require complex API management, extensive integration capabilities, and legacy system support.
- REST API Private:
- Purpose: Private REST APIs are designed to provide RESTful API features accessible only within your Amazon Virtual Private Cloud (VPC).
- Key Features:
- Restricted access: Only available to applications within your VPC or those using a VPN to your VPC.
- Integration with AWS services within a VPC without exposing them to the public internet.
- All features of REST APIs but with private network access.
- Use Case: Best for internal microservices communication, or when the API needs to be exposed to a limited set of internal clients within a private network.
Let’s choose “REST API” for this article,
Click -> “Build“
choose “New API“
API name: Children Library API
choose API endpoint type as -> Regional
Let’s see what are the other types available,
- Regional
- Edge-optimized
- Private
- Regional Endpoint:
- Purpose: A regional API endpoint is intended for clients that are geographically close to the region where the API is deployed.
- Key Features:
- The API is deployed in a specific AWS region.
- Reduced latency for in-region clients, as requests don’t have to travel across regions.
- Ideal for region-specific applications or when most of the API traffic originates from the same region.
- Use Case: Best suited for localized applications where the users are predominantly in the same region as the AWS services.
- Edge-Optimized Endpoint:
- Purpose: Edge-optimized API endpoints are designed for global clients to minimize latency by routing traffic through AWS’s global network of edge locations (part of the Amazon CloudFront content delivery network).
- Key Features:
- Automatically routes client requests to the nearest CloudFront Point of Presence.
- Helps in reducing latency for global clients as requests are handled by edge locations closer to the user.
- Useful for widely distributed client bases.
- Use Case: Ideal for applications with a global user base, needing low latency across different geographical locations.
- Private Endpoint:
- Purpose: A private API endpoint is used within an Amazon Virtual Private Cloud (VPC) to provide secure access to clients within the VPC or connected networks.
- Key Features:
- Accessible only within the VPC or through a secure connection (like VPN or Direct Connect) from on-premises networks.
- Ensures that the API is not exposed to the public internet.
- Integrates with AWS services within a VPC while maintaining privacy and security.
- Use Case: Best for internal APIs, where the services are meant to be accessed only within a corporate network or a controlled environment.
Now, you know which one should choose.
Let’s select the Regional for demo purpose.
click “Create api“
On the Resources page, you will see empty list of routes. So, now we are going to create routes.
Click “Create resource“
We are going to create routes, so now let’s start with health api to check api is healthy or not.
In the Resource name -> health
Below the form, check CORS (Cross Origin Resource Sharing), this will create an additional OPTIONS method that allows all origins, and common header. (Enable this to better avoid any CORS related issues when accessing API from Front-end).
Click “Create resource“
Now click on “/health” -> Let’s create a ‘GET’ method to check
You can see “OPTIONS” method is created.
Click -> Create method
Method Type:-> GET
Integration Type -> Lambda function
Check the toggle option to active related to “Lambda proxy integration“
Lambda function -> check the region where we created the Lambda function and the correct Lambda function respectively.
Keep Default timeout as it is.
Similarly create resources for following APIs
- /book
- CREATE – create / add a new book to the DB
- GET (by ID) – retrieve a book by ID
- PUT/PATCH – update a details of a book
- DELETE – delete from DB
- /books
- GET – get all the books info
Some of the things I can tell you might miss are
- Enable CORS for the resources
- Forgets to check the “Lambda proxy integration” to true
If you created resources, thereafter also you can Enable Cors, just click on any resource -> click ‘Enable CORS‘ under Resource Details.
Similarly, we need to do for other resources as well.
Now let’s deploy the API to the public world.
Click on “Deploy API”
In the Stage field Click “New Stage” then a new field will appeared as “Stage name“
type whatever stage name you want: I am giving as “dev”
then click “Deploy”
Now, go to “Stages” in the sidebar,
Click on the “dev” stage. You will see the Invoke URL. Copy and check in the Postman for all the APIs we have created till now.
But. before testing we need to have the routes handling services in serverless where based on the routes serverless application will process the data whether its creating or updating or retieving data.
So, paste the following JavaScript code in Code source section in Lambda function we created.
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
const dynamodb = new AWS.DynamoDB.DocumentClient();
const tableName = "children_library";
const healthPath = '/health';
const booksPath = '/books';
const bookPath = '/book';
exports.handler = async (event) => {
console.log('request:', JSON.stringify(event));
let response;
switch(true) {
case event.httpMethod === 'GET' && event.path === healthPath:
response = buildResponse(200);
break;
case event.httpMethod === 'GET' && event.path === booksPath:
response = await getBooks();
break;
case event.httpMethod === 'GET' && event.path === bookPath:
response = await getBook(event.queryStringParameters.bookId);
break;
case event.httpMethod === 'POST' && event.path === bookPath:
response = await saveBook(JSON.parse(event.body));
break;
case event.httpMethod === 'PUT' && event.path === bookPath:
response = await updateBook(JSON.parse(event.body));
break;
case event.httpMethod === 'DELETE' && event.path === bookPath:
response = await deleteBook(JSON.parse(event.body));
break;
default:
response = buildResponse(404, '404 Not Found');
}
return response;
}
async function getBooks() {
const params = {
TableName: tableName
}
const allBooks = await scanDynamoRecords(params, []);
const body = {
books: allBooks
}
return buildResponse(200, body);
}
async function getBook(bookId) {
const params = {
TableName: tableName,
Key: {
'id': bookId
}
}
const book = await dynamodb.get(params).promise();
if (book.Item) return buildResponse(200, book.Item);
else return buildResponse(404, 'Book Not Found');
}
async function saveBook(json) {
const params = {
TableName: tableName,
Item: json
}
await dynamodb.put(params).promise();
const body = {
message: "Book Created"
}
return buildResponse(201, body);
}
async function updateBook(json) {
const params = {
TableName: tableName,
Key: {
'id': json.id
},
UpdateExpression: 'set #name = :n, #author = :a, #year = :y, #available = :av',
ExpressionAttributeNames: {
'#name': 'name',
'#author': 'author',
'#year': 'year',
'#available': 'available'
},
ExpressionAttributeValues: {
':n': json.name,
':a': json.author,
':y': json.year,
':av': json.available
}
}
await dynamodb.update(params).promise();
const body = {
message: "Book Updated"
}
return buildResponse(200, body);
}
async function deleteBook(json) {
const params = {
TableName: tableName,
Key: {
'id': json.id
}
}
await dynamodb.delete(params).promise();
const body = {
message: "Book Deleted"
}
return buildResponse(200, body);
}
function buildResponse(statusCode, body) {
return {
statusCode: statusCode,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(body)
}
}
async function scanDynamoRecords(scanParams, itemArray) {
try {
const dynamoData = await dynamodb.scan(scanParams).promise();
itemArray = itemArray.concat(dynamoData.Items);
if (dynamoData.LastEvaluatedKey) {
scanParams.ExclusiveStartKey = dynamoData.LastEvaluatedKey;
return await scanDynamoRecords(scanParams, itemArray);
}
return itemArray;
} catch(error) {
console.log(error);
}
}
Try to Insert a data into DB
Get by bookId:
Hope you enjoyed this article, We have just developed a Simple App with basic CRUD operations.
I am planning to implement a small sized working level application with Authentication which uses AWS Cognito, Uploading files to AWS S3 and using some AWS services.
Let’s see
Leave a Reply