Function URLs - Quick and Easy way to Invoke AWS Lambda Functions over HTTP

A Function URL is a dedicated endpoint for your Lambda function. Learn how to enable Function URLs and build an API using .NET Lambda Function.

Rahul Pulikkot Nath
Rahul Pulikkot Nath

Table of Contents

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

A Function URL is a dedicated HTTP(S) endpoint for your Lambda function.

When enabling Function URLs, Lambda automatically generates a unique URL endpoint. Once you create a function URL, its URL endpoint never changes.

Function URLs are a good choice for a single-function microservice with a public endpoint that doesn’t require the advanced functionality of API Gateway.

In this post, let’s learn,

  • Create a Lambda Function using .NET and enable Function URLs.
  • Handle request/response from the Lambda URL endpoint.
  • Handle different HTTP requests and build an API endpoint using Function URLs.

Enable Function URL on .NET Lambda Function

Let's first create a new Lambda Function to see Function URLs in action. For this example, I will make one using .NET. But the process is the same regardless of which programming language you choose.

Create a .NET Lambda Function

Install the AWS Toolkit (Visual Studio, Rider, VS Code) and set up Credentials for the local development environment. The toolkit helps you quickly develop .NET applications on the AWS infrastructure and deploy them quickly to your AWS Accounts.

With the AWS Toolkit installed, select the AWS Lambda Lambda Project template from the new project template and choose the Empty Function blueprint. This creates an AWS Lambda function template project with a Function.cs class.

To handle requests coming from Function URLs, the Lambda input must be of type APIGatewayHttpApiV2ProxyRequest. This is the same input type required when building HTTP APIs (v2 payload) on Amazon API Gateway.

Update the input type; for now, we will return the body coming in as part of the request.

public string FunctionHandler(
    APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
{
    return request.Body.ToUpper();
}

Deploy Lambda Function

To publish the Lambda function to an AWS account, you can use the Publish to AWS Lambda option that the AWS Toolkit provides. You can directly publish the new lambda function with your development environment connected to your AWS account.

I prefer to set up an automated build/deploy pipeline to publish to your AWS Account for real-world applications. Check out the below for an example of how to set up a Lambda Function deployment pipeline from GitHub Actions.

Enable Function URL for AWS Lambda

Once deployed, we can enable Function URLs on our Lambda Function. Under Configuration, navigate to the Function URL section.

Click on Create function URL button to create a new Function URL for the Lambda function. This prompts a new window to select the Authenticate type for the Function URL.

Function URLs support two Auth types -> AWS_IAM and NONE. For this post, we will use NONE, which means Lambda won't perform any authentication and is accessible to anyone with the URL.

In a future post, we will cover how to enable AWS_IAM authentication on Function URLs and protect the Lambda function.

Once enabled and saved, the Lambda environment automatically generates a new URL. This URL can be used to invoke the Lambda Function.

To test the integration, make a POST request to the Function URL with some random text in the body. It will return the text in upper case.

Handle Request/Response from Function URL

By default, when returning a string response from the Lambda Function handler, it automatically converts it into a 200 OK HTTP response.

To have finer control over the returned HTTP Status code and response content, we can update the Function handler to return a APIGatewayHttpApiV2ProxyResponse object.

public APIGatewayHttpApiV2ProxyResponse FunctionHandler(
    APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
{
    string? userIdString = null;
    request.QueryStringParameters?.TryGetValue("userId", out userIdString);

    if (!Guid.TryParse(userIdString, out Guid userId)) return BadRequest();

    var user = new User() { Id = userId, Name = "<Name from Database>" };

    return new APIGatewayHttpApiV2ProxyResponse()
    {
        Body = JsonSerializer.Serialize(user),
        StatusCode = (int)HttpStatusCode.OK
    };
}

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

The above Lambda Function handler extracts userId from  QueryStringParameters on the incoming request. If the id exists, it returns a mocked User object. It returns a BadRequest response with the appropriate Status code if it is not passed.

Build an API using Function URL in .NET

The above version of the Lambda function returns the appropriate response no matter which HTTP Verb is used.
But when building HTTP APIs, we need to use the HTTP Verbs to determine what action to perform.

For example, for a GET request, we can return the User corresponding the userId. For a POST request, we can take the User information from the request body, and create a new User in our data store, and so on.

We can easily do this using the request.RequestContext.Http.Method property on the incoming request object. Based on the HTTP Verb of the request, we can switch between the different behavior in our Lambda Function.

public APIGatewayHttpApiV2ProxyResponse FunctionHandler(
    APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
{
    return request.RequestContext.Http.Method.ToUpper() switch
    {
        "GET" => HandleGetRequest(request),
        "POST" => HandlePostRequest(request),
        _ => BadRequest()
    };
}

private APIGatewayHttpApiV2ProxyResponse HandlePostRequest(
    APIGatewayHttpApiV2ProxyRequest request)
{
    var user = JsonSerializer.Deserialize<User>(request.Body);
    if (user == null) return BadRequest();

    // Add this to the datastore

    return new APIGatewayHttpApiV2ProxyResponse()
    {
        StatusCode = (int)HttpStatusCode.Accepted,
        Body = user.Id.ToString()
    };
}

The above version of the Lambda Function handles GET and POST requests and handles them appropriately to return or create a new User.

Expanding on this, we can also handle the other HTTP Verbs to complete our API endpoint. I will leave this as an exercise for you, my reader.

I hope this helps you get started with AWS Lambda Function URLs. We saw how we could quickly build and deploy a simple API endpoint using Function URLs in .NET. In a future blog post, I will show you how to authenticate the Function URLs so that it is accessible only to your application or authorized users.

AWSServerlessDotnet