Building a Production-Ready CRUD Service with AWS in Under 100 Lines

Using AWS and the Serverless Framework to create a backend for an e-commerce service with code snippetsIntroductionWhen I first starting using Serverless on AWS a few years ago, I turned to the Serverless Framework to provision the resources needed for…


This content originally appeared on Level Up Coding - Medium and was authored by Josh Thorne

Using AWS and the Serverless Framework to create a backend for an e-commerce service with code snippets

Introduction

When I first starting using Serverless on AWS a few years ago, I turned to the Serverless Framework to provision the resources needed for my services. These days, I use the AWS Cloud Development Kit (CDK) for most of my projects, but for smaller, fast-moving applications, the Serverless Framework remains a relevant and effective tool.

In this article, I’ll walk you through how the Serverless Framework can help you rapidly build a scalable, production-ready CRUD service. With its high level of abstraction, the Serverless Framework simplifies infrastructure management, allowing you to focus on your application’s core functionality. Leveraging AWS-managed services like AWS Lambda and DynamoDB, we can create this service in under 100 lines of code.

In this example, we’ll build the backend for a simple e-commerce application capable of storing and retrieving products using a DynamoDB database.

Why Serverless and the Serverless Framework?

Serverless is a cloud computing model where the cloud provider (in this case, AWS) manages the infrastructure. You no longer need to worry about server maintenance, patching, or scalability — AWS takes care of it for you. This “pay-as-you-go” model is cost-effective and perfect for applications that experience unpredictable traffic patterns, making it an ideal solution for both startups and established enterprises.

The Serverless Framework is an Infrastructure-as-Code (IaC) tool that provides a higher level of abstraction for Serverless services, making it incredibly fast and easy to deploy resources on AWS. With a few simple commands, you can have a fully managed, scalable backend without manually configuring individual AWS services.

Tip: The Serverless Framework is free unless you’re part of an organisation generating over $2 million annually. For details, check out their pricing page.

Architecture

In this setup, we’ll use three fully managed AWS services to create a simple, scalable CRUD application:

  • Amazon API Gateway: Acts as the routing layer, managing HTTP requests.
  • AWS Lambda: Executes the compute logic, handling requests and interacting with DynamoDB.
  • Amazon DynamoDB: Stores product data in a highly available, scalable NoSQL database.
Architecture diagram for our simple e-commerce CRUD application

How the Architecture Works

  1. Add Product (POST Request):
  • A client sends a POST request to the /products API Gateway endpoint with product details.
  • API Gateway forwards the request to the add-product Lambda function.
  • The function processes the request and writes the product details to the DynamoDB table.
  • A success response is returned to the client confirming the addition.

2. Retrieve Product (GET Request):

  • A client makes a GET request to the /products API Gateway endpoint, passing the product ID as a query parameter.
  • API Gateway routes the request to the get-product Lambda function.
  • The Lambda function queries DynamoDB for the product with the specified ID.
  • The product details are returned to the client as a JSON response.

Key Benefits

  • Fully Managed: Lambda, API Gateway, and DynamoDB are fully managed services, so you don’t need to worry about scaling, patching, or server maintenance.
  • Scalability: Each component scales automatically based on demand, ensuring that your service can handle traffic spikes without performance degradation.
  • Cost Efficiency: With AWS’s pay-as-you-go model, you only pay for what you use, making it ideal for applications with fluctuating traffic.
  • High Availability: AWS’s built-in fault tolerance ensures high availability, with DynamoDB replicating data across multiple Availability Zones.

Getting Started

Prerequisites

Setting up the Codebase

  1. Install the Serverless Framework globally by running the following command npm i serverless -g
  2. Once installed, log in to your Serverless account from the command line: serverless login. This command will open a browser window where you can sign in to your account.
  3. After logging in, you can proceed to set up your application by running: serverless. Follow the prompts and select the following options:
  • Select A Template: AWS / Node.js / HTTP API
  • Name Your project: Choose a meaningful name for your service
  • Create Or Select An Existing App: Create a new application
  • Configure AWS credentials: Select your preferred method for configuring AWS credentials
Tip: you can use sls as a shorthand for the serverless command

This will generate the default handler.js and serverless.yml files. We’ll now modify them to build our CRUD service.

Serverless Framework generated files

Building the CRUD Service

We’ll need two AWS Lambda functions for this project: one to add products and another to retrieve them. You can delete the default handler.js file and create two new files: add-product-handler.js and get-product-handler.js.

First, initialise a package.json by running:

npm init

We’ll be using the AWS SDK to interact with DynamoDB, so install it by running:

npm i aws-sdk

Writing Lambda Functions

In this section, we’ll write two AWS Lambda functions: one to add products to DynamoDB and another to retrieve them. Both functions will be triggered by HTTP requests via API Gateway.

Here’s the code for the add-product-handler.js to add products to DynamoDB:

const AWS = require("aws-sdk");
const dynamoDb = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
const requestBody = JSON.parse(event.body);
const params = {
TableName: "ecommerce-table",
Item: {
id: requestBody.id,
...requestBody,
},
};

await dynamoDb.put(params).promise();
return {
statusCode: 200,
body: JSON.stringify({
message: "Item successfully written to DynamoDB",
}),
};
};
  • AWS SDK: We start by importing the AWS SDK and initialising the DocumentClient to interact with DynamoDB.
  • Parsing the event body: The incoming event (an HTTP request via API Gateway) contains the product details in the body. We parse this body using JSON.parse(event.body) to access the product data.
  • DynamoDB put operation: We prepare the parameters for the put operation. The Item field contains the product data, including a unique id and other attributes from the request body.
  • Writing to DynamoDB: We use dynamoDb.put(params).promise() to add the product to the DynamoDB table asynchronously.
  • Return response: If successful, the function returns a 200 OK response, with a message confirming that the product was added to the database.
Tip: Defining the DynamoDB client outside the handler reuses it during “warm” Lambda invocations, reducing latency.

And here’s the get-product-handler.js to retrieve products:

const AWS = require("aws-sdk");
const dynamoDb = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event) => {
const { id } = event.queryStringParameters;
const params = {
TableName: "ecommerce-table",
Key: {
id: id,
},
};

const data = await dynamoDb.get(params).promise();
if (data.Item) {
return {
statusCode: 200,
body: JSON.stringify(data.Item),
};
} else {
return {
statusCode: 404,
body: JSON.stringify({
message: "Item not found",
}),
};
}
};
  • Extracting the product ID: We retrieve the product ID from the query string parameters (event.queryStringParameters) of the HTTP request.
  • DynamoDB get operation: The params object defines the table name and the primary key (id) to identify the product we want to retrieve.
  • Fetching from DynamoDB: We use dynamoDb.get(params).promise() to fetch the item. If the item is found, it is returned with a 200 OK response.
  • Handling missing items: If no product is found for the given ID, the function returns a 404 Not Found status code along with an error message.
The file structure after the changes

Defining the serverless.yml File

The serverless.yml file is the core configuration for your Serverless Framework project. It defines everything from the provider (AWS) to the Lambda functions and other AWS resources (such as DynamoDB tables) your service will interact with. Below is the configuration for our e-commerce backend.

org: myorg
app: serverless-ecommerce-app
service: serverless-ecommerce

provider:
name: aws
runtime: nodejs20.x # Node.js runtime version for Lambda functions
region: eu-west-1 # AWS region where resources will be created

# IAM permissions required for Lambda functions to access DynamoDB
iamRoleStatements:
- Effect: "Allow"
Action:
- "dynamodb:GetItem"
- "dynamodb:PutItem"
Resource:
- "arn:aws:dynamodb:${self:provider.region}:*:table/ecommerce-table" # Resource ARN for the DynamoDB table

resources:
# Resources section allows you to define additional AWS resources (like DynamoDB tables).
Resources:
YourTableName:
Type: "AWS::DynamoDB::Table"
Properties:
TableName: "ecommerce-table" # Name of the DynamoDB table
AttributeDefinitions:
- AttributeName: "id" # Primary key attribute (product ID)
AttributeType: "S" # Type of the attribute (String)
KeySchema:
- AttributeName: "id"
KeyType: "HASH" # 'id' is the partition key
ProvisionedThroughput: # Read and write capacity units for the table
ReadCapacityUnits: 1
WriteCapacityUnits: 1

functions:
# Lambda function to retrieve a product from the DynamoDB table
getProduct:
handler: get-product-handler.handler # Points to the handler function in get-product-handler.js
events:
- httpApi:
path: /products # API Gateway route to invoke this function
method: get # HTTP GET method

# Lambda function to add a product to the DynamoDB table
addProduct:
handler: add-product-handler.handler # Points to the handler function in add-product-handler.js
events:
- httpApi:
path: /products # API Gateway route to invoke this function
method: post # HTTP POST method

Provider

The iamRoleStatements section grants specific permissions to our Lambda functions. In this case, the functions are allowed to read (dynamodb:GetItem) and write (dynamodb:PutItem) to the DynamoDB table named ecommerce-table.

Tip: Use the serverless-iam-roles-per-function plugin to assign a separate IAM role per function for better security. More details in the Serverless Framework documentation.

Resources

In the resources block, we define a DynamoDB table named ecommerce-table with id as its partition key. The throughput is set to 1 read and 1 write capacity unit, which can be adjusted based on expected load.

Functions

  • getProduct: This function is tied to the get-product-handler.handler file, and it is triggered via an HTTP GET request at the /products endpoint.
  • addProduct: This function is linked to add-product-handler.handler and is triggered via an HTTP POST request at the same /products endpoint.

Deploying Your Service

Now that your Lambda functions are set up, you’re ready to deploy the application. Run the following command:

serverless deploy

This will deploy your service to AWS, and once completed, you’ll see the endpoints for your CRUD operations displayed in the terminal.

Deploying "serverless-ecommerce" to stage "dev" (eu-west-1)

✔ Service deployed to stack serverless-ecommerce-dev (67s)

endpoints:
GET - https://y17nlwggq0.execute-api.eu-west-1.amazonaws.com/products
POST - https://y17nlwggq0.execute-api.eu-west-1.amazonaws.com/products
functions:
getProduct: serverless-ecommerce-dev-getProduct (19 MB)
addProduct: serverless-ecommerce-dev-addProduct (19 MB)

⠇ Finishing(node:24853) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)

Testing Your Service

Once your service is deployed, you can start adding and fetching products from your DynamoDB table. Here’s how to test your service:

  1. Retrieve Your API Endpoint
    After deployment, the Serverless Framework will display the API endpoint in your terminal output. For example, your endpoint might look something like:
    https://y17nlwggq0.execute-api.eu-west-1.amazonaws.com/products.
  2. Adding an Item (POST Request)
    To add an item to your DynamoDB table, perform a POST request to the /products endpoint. You can use tools like cURL, Postman, or any HTTP client to send the request. Here’s an example of a POST request using cURL:
curl --location 'https://y17nlwggq0.execute-api.eu-west-1.amazonaws.com/products' \
--header 'Content-Type: application/json' \
--data '{
"id": "unique-socks-id",
"name": "Black cotton socks",
"price": 12,
}'

Be sure to replace the sample endpoint with your own. If the request is successful, you should receive a response indicating the item was added to DynamoDB.

3. Validating the Item in DynamoDB

To confirm that the item has been successfully added, follow these steps:

  • Open the AWS DynamoDB Console.
  • In the left-hand navigation panel, click on Tables.
  • Select your table (ecommerce-table).
  • Click Explore table items.
  • You should now see the item you just added in the table.

4. Retrieving an Item (GET Request)
You can also retrieve the item you just added by making a GET request to the /products endpoint with the item's ID as a query parameter. Here’s an example using cURL:

curl --location 'https://y17nlwggq0.execute-api.eu-west-1.amazonaws.com/products?id=unique-socks-id'

This will return the product in JSON format:

{   "id": "unique-socks-id",   "name": "Black cotton socks",   "price": 12 }

By following these steps, you can confirm that your CRUD service is working correctly. Both the add (POST) and fetch (GET) operations should return successful responses, and you should be able to verify the data directly in your DynamoDB table.

Next Steps

While this architecture provides scalability and fault tolerance, it’s essential to make several improvements before using it for production workloads. Here are a few critical steps to consider:

  • Implement Authentication and Authorisation
    Currently, the endpoints are publicly accessible, allowing anyone to add or retrieve items from your database. To secure your API, integrate proper authentication mechanisms such as AWS Cognito, API Gateway authorisers, or an OAuth2 solution to ensure only authorised users can access your endpoints.
  • Enhance Fault Tolerance and Error Handling
    Though this setup interacts with DynamoDB, it lacks proper error handling and fault tolerance mechanisms. Add more robust error handling to gracefully manage unexpected inputs, DynamoDB service failures, or network timeouts. Consider retry logic or circuit breakers to improve system resilience.
  • Apply Security Measures
    To protect your service from potential attacks, consider implementing additional security layers such as AWS WAF (Web Application Firewall) to block malicious requests. Additionally, enforce rate limiting to prevent abuse, such as brute force attacks or DDoS attempts.
  • Optimise your Service
    Change the configuration of your services to ensure that they perform at their peak condition. You can use various services to optimise your lambda functions.

By incorporating these changes, you’ll be well on your way to building a secure, production-ready service capable of handling production-level traffic with minimal vulnerabilities.

Conclusion

With the Serverless Framework and less than 100 lines of code, we’ve set up a scalable, production-ready CRUD service backed by AWS Lambda and DynamoDB. Whether you’re building a quick proof of concept or launching a small-scale service, this approach minimises infrastructure management while leveraging the scalability and reliability of AWS.

If you found this article useful, please clap, follow or connect with me today! 👏
Photo by Ilya Pavlov on Unsplash

Building a Production-Ready CRUD Service with AWS in Under 100 Lines was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Josh Thorne


Print Share Comment Cite Upload Translate Updates
APA

Josh Thorne | Sciencx (2024-09-24T16:47:46+00:00) Building a Production-Ready CRUD Service with AWS in Under 100 Lines. Retrieved from https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/

MLA
" » Building a Production-Ready CRUD Service with AWS in Under 100 Lines." Josh Thorne | Sciencx - Tuesday September 24, 2024, https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/
HARVARD
Josh Thorne | Sciencx Tuesday September 24, 2024 » Building a Production-Ready CRUD Service with AWS in Under 100 Lines., viewed ,<https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/>
VANCOUVER
Josh Thorne | Sciencx - » Building a Production-Ready CRUD Service with AWS in Under 100 Lines. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/
CHICAGO
" » Building a Production-Ready CRUD Service with AWS in Under 100 Lines." Josh Thorne | Sciencx - Accessed . https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/
IEEE
" » Building a Production-Ready CRUD Service with AWS in Under 100 Lines." Josh Thorne | Sciencx [Online]. Available: https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/. [Accessed: ]
rf:citation
» Building a Production-Ready CRUD Service with AWS in Under 100 Lines | Josh Thorne | Sciencx | https://www.scien.cx/2024/09/24/building-a-production-ready-crud-service-with-aws-in-under-100-lines/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.