Building Cost-Effective Serverless APIs with AWS Lambda, API Gateway, and MongoDB in .NET
Introduction
In today’s tech world🌎, serverless architecture is changing how we build apps. It helps you scale easily, saves money, and eliminates the need to manage servers. A big part of this change is AWS Lambda, a service from Amazon that lets you run code without worrying🤔 about the servers behind it. When combined with Amazon API Gateway, you can quickly create powerful APIs without any server management.🐧ྀིྀི
In this blog series, we’ll walk through how to create a serverless API using AWS Lambda, API Gateway, and MongoDB, all in .NET. We’ll take a hands-on approach, showing you how these tools work together to build an API that’s fast, flexible, and ready for the cloud☁. Serverless solutions like this are becoming super popular in modern software development.⚛️
Throughout this series, you’ll learn👨🏼💻 how these services work together, how to improve your code, and how to make the most of the cloud to build APIs that are both efficient and cost-effective.
So, get ready⏳ to explore serverless development and let’s create a great RESTful API with AWS Lambda, API Gateway, and MongoDB using .NET!☁
For this series, we’ll be using Visual Studio 2022 as our development environment, making it easier to work with .NET and integrate seamlessly with AWS services.
Now, let’s get started!!!😊
- Open Visual Studio and create a new project. From the list of project templates, select AWS Lambda Project. If you don’t see the AWS Lambda template, make sure you’ve installed the AWS Toolkit for Visual Studio. You can download it from the Visual Studio Marketplace or directly from AWS.
2. Give Your Project Name✨
3. Select a Empty Function and hit Finish
4. Go to solution Explorer and Right Click on Employee_records
5. Click On Manage NuGet Packages
6. Install ApiGatewayEvents
7. Install DynamoDb package
8. Now Go TO SolutionExplorer Right Click Employee_Record and Add a New Class.
- Select Class and Give a Name here employeees
- This code defines a class
Employees
that represents an employee record in a DynamoDB table using the AWS SDK for .NET.
Here’s a breakdown of what’s happening😇
using
Statements: These are necessary namespaces for the code, including those for Amazon DynamoDB and basic .NET functionality.[DynamoDBTable("employees")]
: This attribute marks the class as a DynamoDB entity. It specifies that this class will be mapped to the DynamoDB table called "employees".Class Definition (
Employees
): This is the model for employee records in the DynamoDB table.[DynamoDBHashKey("id")]
: This attribute marks theId
property as the primary key (hash key) for the DynamoDB table. It uniquely identifies each employee record.[DynamoDBProperty("First_name")]
,[DynamoDBProperty("Last_name")]
, and[DynamoDBProperty("Address")]
: These attributes map the properties in the class to the corresponding attribute names in the DynamoDB table.Properties:
Id
: The employee's unique identifier (nullableInt32
).FirstName
,LastName
, andAddress
: Represent the employee's first name, last name, and address (all nullablestring
types).
This code is a basic structure for working with DynamoDB in a .NET application, where employee records are mapped to a DynamoDB table for operations.
Now Creating another Class Function.cs For Our LambdaFunction
This code defines an AWS Lambda function that interacts with a DynamoDB table to manage employee records. It uses Amazon API Gateway to handle HTTP requests (GET and POST). Here's a breakdown:
Imports:
Amazon.DynamoDBv2: Provides access to AWS DynamoDB.
Amazon.Lambda.APIGatewayEvents: Handles API Gateway events for Lambda.
Amazon.Lambda.Core: Provides the core Lambda function interface.
Newtonsoft.Json: Used for JSON serialization and deserialization.
Lambda Serializer:
- The LambdaSerializer attribute tells AWS Lambda to use the SystemTextJson serializer for converting JSON input and output.
FunctionHandler:
The main function receives API Gateway requests and processes them based on the route.
GET /employees: If the route matches GET /employees, it performs a Scan operation on the DynamoDB
Employees
table and returns all employee records.POST /employees: If the route is POST /employees, it deserializes the request body (which should be a JSON representation of an employee), saves it to DynamoDB, and returns a success message with the employee ID. If there’s a JSON deserialization error, it returns a 400 Bad Request with an error message.
If neither of these conditions is met (unsupported route or missing body), it returns a 400 Bad Request.
Logging:
- It logs the request body for POST /employees to help with debugging.
Key Operations:
ScanAsync: Retrieves all items from the
Employees
table.SaveAsync: Saves a new employee record to the table.
The function is a simple serverless API handler that can create and read employee records using AWS Lambda and DynamoDB.
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
using System.Net;
using Newtonsoft.Json;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace Employee_records;
public class Function
{
/// <summary>
/// A simple function that takes a string and does a ToUpper
/// </summary>
/// <param name="input">The event for the Lambda function handler to process.</param>
/// <param name="context">The ILambdaContext that provides methods for logging and describing the Lambda environment.</param>
/// <returns></returns>
public static async Task<APIGatewayHttpApiV2ProxyResponse> FunctionHandler(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
{
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
DynamoDBContext dbcontext = new DynamoDBContext(client);
if (request.RouteKey.Contains("GET /employees"))
{
var data = await dbcontext.ScanAsync<Employees>(default).GetRemainingAsync();
return new APIGatewayHttpApiV2ProxyResponse
{
StatusCode = (int)HttpStatusCode.OK,
Body = System.Text.Json.JsonSerializer.Serialize(data),
};
}
else if (request.RouteKey.Contains("POST /employees") && request.Body != null)
{
context.Logger.LogLine($"Request Body: {request.Body}"); // Log the incoming JSON
try
{
var employee = System.Text.Json.JsonSerializer.Deserialize<Employees>(request.Body);
await dbcontext.SaveAsync(employee);
return new APIGatewayHttpApiV2ProxyResponse
{
StatusCode = (int)HttpStatusCode.OK,
Body = $"Employee with Id {employee?.Id} added successfully",
};
}
catch (JsonException ex)
{
context.Logger.LogLine($"JSON Deserialization Error: {ex.Message}");
return new APIGatewayHttpApiV2ProxyResponse
{
StatusCode = (int)HttpStatusCode.BadRequest,
Body = "Invalid JSON format",
};
}
}
else
{
return new APIGatewayHttpApiV2ProxyResponse
{
StatusCode = (int)HttpStatusCode.BadRequest,
Body = "Unsupported route or missing body",
};
}
}
}
- Go to Solution Explorer > right-click on Employee_records > and select Publish to AWS Lambda. Then, provide the >function name. In this case, I’m using >Employees_Record and Hit > Next.
- Select BasicExecutionRole
- The Lambda function has been published.
- Now, go to your AWS Management Console, search for Lambda, and open it. You should see Employees_Record, which is our function name.
- Go to Configuration, then select Permissions, and click on the Role name
17. Click on Add permission.
18. Click On Attach Policies
- Select DynamoDBFullAccess. However, do not grant FullAccess🔓 in a production environment for security reasons.🔐
20. Now, we’re granting permissions to our Lambda function to access DynamoDB.
21. Now, go to your DynamoDB and create a table.
- Give the table a name; here, I'm using employees. Set the Partition key to ID.
23. Now, click Create Table.
24. Now, we’ve created the employees table in DynamoDB.
- Now, go to API Gateway and click on Create API.
- Select HTTP API and click Build.
- Give your API a name; here, I’m using employee-api.
- Now Configure Routes hit Next
- Click Next
- Clicked Create
31. The employee-api has been created.
32. Now, click on Routes and hit Create.
33. Choose the GET method and set the path to /employees.
- Click Attach integration
35. Click Create and attach an integration
- Select the Lambda Function and click Create.
- Select your Lambda function, Employees_Record.
- Click on Create
- Successfully attached the integration to the routes.
- Click on the API and paste the URL. It will show a empty[] screen because there is no data in the table yet
- Now go to Your table employees and insert some data
- You can see your data, which means the GET method is working fine.
43. Similarly, go ahead and create a POST method in API Gateway.
- "Pass some data and hit the API URL using the POST method. For this, I am using Postman. You should see that the data was successfully added, and a 200 status code is returned."
45. "Now, go to your API URL and access it. You should see your data, which means the POST method i is working fine."
As you can see, we’ve successfully🎉 performed both GET and POST methods in this tutorial✅. In future tutorials, I’ll demonstrate other methods as well. This is a simple example of how we can build an API, and we’ll dive deeper into more advanced concepts in the upcoming series.⭐
Thank you 🙏for taking the time to hope you follow along! I appreciate you being here, and I hope you found the tutorial useful. Looking forward to sharing more with you in the upcoming series. See you next time!🍀