Learn How AWS Lambda Annotations Framework Makes API Gateway Integration Easy.

Let's learn what the Lambda Annotation Framework aims to solve and how it compares with older ways of building Lambda Functions. We will see this through an example of creating a simple API endpoint.

Rahul Pulikkot Nath
Rahul Pulikkot Nath

Table of Contents

The Lambda Annotations Framework is a programming model that makes it easier to build AWS Lambda Functions using .NET.

It provides a natural programming model to build Lambda Functions.

The framework uses C# custom attributes and Source Generators to translate annotated Lambda functions to the regular Lambda programming model.

This article is sponsored by AWS and is part of my AWS Series.

In this blog post let's learn what the Lambda Annotation Framework aims to solve and how it compares with older ways of building Lambda Functions. We will see this through an example of creating a simple API endpoint.

First, we will build this using the traditional integration with API Gateway with a Lambda Function. We will then see how the Annotations Framework makes the process seamless and more straightforward.

API Endpoint Using Lambda Programming Model

There are several ways to build APIs on AWS Lambda, including using API Gateway, Function URLs, and hosting ASP.NET Core Applications.

Below is a sample Lambda Function MathPlus that integrates with API Gateway to add two numbers.

It expects the numbers to add as path variables - /{a}/{b}

public APIGatewayProxyResponse MathPlus(
    APIGatewayProxyRequest request, ILambdaContext context)
{
    if (!request.PathParameters.TryGetValue("a", out var aString) ||
        !request.PathParameters.TryGetValue("b", out var bString))
        return BadRequest();

    if (!int.TryParse(aString, out var a) || !int.TryParse(bString, out var b))
        return BadRequest();

    var sum = a + b;
    
    return OkResult(sum);
}

private static APIGatewayProxyResponse OkResult(int sum)
{
    return new APIGatewayProxyResponse
    {
        StatusCode = (int)HttpStatusCode.OK,
        Body = sum.ToString(),
        Headers = new Dictionary<string, string>
        {
            { "Content-Type", "text/plain" }
        }
    };
}

private static APIGatewayProxyResponse BadRequest()
{
    return new APIGatewayProxyResponse
    {
        StatusCode = (int)HttpStatusCode.BadRequest
    };
}

The Function code tries to retrieve the 2 numbers (a & b) from the PathParameters property on the APIGatewayProxyRequest. It then tried to convert them into an integer.

If the numbers are valid integer values, it returns the sum of them as a OkResult, if not it returns a BadRequest.

The Function code is verbose and tied to the API Gateway Request/Response models.

⚠️
 The actual logic that we are interested in is only one line (adding the two numbers) and the rest of it is purely boilerplate code.

API Endpoint Using Lambda Annotations Framework

Lambda Annotations Framework simplifies the default Lambda programming model and simplifies the Function code.

It removes the boilerplate code required to integrate with API Gateway.

💡
The Annotations Framework uses C# Source Generators to generate the boilerplate code at compile time. 

Since this happens at compile time, there is no performance overhead in using Lambda Annotations Framework to build Lambda Functions.

To start using the Lambda Annotation Framework, add a reference to Amazon.Lambda.Annotations package.

The LambdaFunction attribute is used the decorate a .NET Function as a Lambda Function.

Depending on the type of API we are building in API Gateway, we can use the RestApi or the HttpApi attribute.

The below Function implements the same API endpoint to add two numbers using the Annotations Framework.

[LambdaFunction]
[HttpApi(LambdaHttpMethod.Get, "/add/{a}/{b}")]
public int Add(int a, int b, ILambdaContext lambdaContext)
{
    return a + b;
}

As you can see it is more close to the .NET programming language, which takes in two integers as the Function's input and returns the sum of them as the result.

Annotations Framework Generated Code

All the boilerplate code is hidden by the function's attributes.

Below is a screenshot of the DLL from JetBrains dotPeek application, showing the disassembled code of the Lambda Function generated by the C# Source Generators in the Annotations library.

Screenshot of the Function DLL in JetBrains dotPeek showing the disassembled code of the function generated by the C# source generato.

The generated source is very similar to the Lambda Function we wrote with the original Lambda Programming Model.

Annotations Framework Generated CloudFormation Template

The .NET Annotations Framework also generates a CloudFormation template to make it easier to deploy the Lambda Function Code.

By default, it generates a file with the name serverless.template, which is defined in the aws-lambda-tools-defaults.json file as the template property.

Below is the generated CloudFormation template for the Function that we wrote above to add two numbers.

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "This template is partially managed by Amazon.Lambda.Annotations (v1.0.0.0).",
  "Resources": {
    "AnnotationScratchFunctionAddGenerated": {
      "Type": "AWS::Serverless::Function",
      "Metadata": {
        "Tool": "Amazon.Lambda.Annotations",
        "SyncedEvents": [
          "RootGet"
        ]
      },
      "Properties": {
        "Runtime": "dotnet6",
        "CodeUri": ".",
        "MemorySize": 512,
        "Timeout": 60,
        "Policies": [
          "AWSLambdaBasicExecutionRole"
        ],
        "PackageType": "Zip",
        "Handler": "AnnotationScratch::AnnotationScratch.Function_Add_Generated::Add",
        "Events": {
          "RootGet": {
            "Type": "HttpApi",
            "Properties": {
              "Path": "/add/{a}/{b}",
              "Method": "GET"
            }
          }
        }
      }
    }
  }
}

It sets up an API Gateway HTTP API endpoint with the appropriate path and sets up the AWS Lambda Function integration to the source-generated function name - AnnotationScratch::AnnotationScratch.Function_Add_Generated::Add.

You can deploy the CloudFormation template from your IDE or also from the command line. It creates all the resources as part of a Stack and deploys the Lambda Function, the API Gateway, and the endpoint as part of it.

It also sets up the IAM policies for the Lambda Function and enables API Gateway to invoke the Lambda Function.  

Building API Endpoints backed by Lambda Functions has never been this easy. Hope this gives you a good introduction to get started using the Lambda Annotations Framework.

We will learn how to build a CRUD API using the Lambda Annotations Framework in an upcoming post.

AWS