Understanding the AWS Lambda Runtime API
Tags: serverless, aws, lambda, go
Published: 2021-06-19 08:33:17

---
title: Understanding the AWS Lambda Runtime API
published: true
description: What is and how the AWS Lambda Runtime works.
tags: serverless, aws, lambda, go
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzdmh9aesh6pniixcce5.png
---
Many parts make the AWS Lambda running. Behind an arguably straightforward interface of a _AWS Lambda_ handler stands a complex machinery that handles scaling, networking, isolation, and security.

The release of _AWS Lambda extensions_ revealed some parts of that system to us, mere mortals using the platform. I'm talking about the _Lambda Runtime API_, the _Lambda Logs API_ and the _Lambda Extensions API_.

This blog will focus on the _Lambda Runtime API_ exploring what the _Lambda Runtime API_ is and how it's used by the _Lambda runtime_ your functions are operating on.

### What the Runtime API is

In the context of _AWS Lambda_, the _Runtime API_ is a bridge between the _Lambda Service_ and the _Lambda runtime_ your function is deployed with.

The _Lambda Runtime API_ communicates with _Lambda runtime_ through an HTTP layer.
This way of communication contrasts with how "older" runtimes operate. For example, the `go1.x` runtime uses a UDP transport layer with a proprietary data encoding scheme specific to the Go programming language (I suspect this is why the `go1.x` runtime does not support _Lambda extensions_).

The _Lambda runtime_ pools the _Runtime API_ for events then invokes your code with that event. Since the communication is bi-directional, the _Lambda runtime_ communicates the invocation results and errors back to the _Runtime API_. Here is an illustration of how the flow looks like from my point of view.

> Please note that this illustration is most likely a simplification. I'm in no way associated with the AWS Lambda team, and I do not know the service's technical inner workings. This is my mental model of how the _AWS Lambda Runtime API_ communicates with _Lambda runtime_.

![Communication](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kk71h861i1xpa7tq4lj7.png)

### Custom runtimes and the Runtime API

The described communication mechanism between the _Lambda runtime_ and the _Runtime API_ enables us to create custom _Lambda runtimes_ with relative ease. The first time I saw that it is possible to [use bash as a _Lambda runtime_ my mind was blown](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-walkthrough.html).

AWS has open-sourced some of the _Lambda runtimes_ you might be using on a day-to-day basis. You can find the [Go](https://github.com/aws/aws-lambda-go), [Python](https://github.com/aws/aws-lambda-python-runtime-interface-client) and [Node](https://github.com/aws/aws-lambda-nodejs-runtime-interface-client) _Lambda runtimes_ on their GitHub. I encourage you to go out and explore those repositories. There is much to learn about how the code you are writing and deploying is run.

### Local development of custom runtimes

When developing in the cloud, there is no substitute for running your code in a real environment and communicating with actual services.
While this also stands true for developing custom _Lambda runtime_, there are two ways of emulating the _Lambda Runtime API_ locally to shorten the feedback loop in-between deployments.

#### Using AWS SAM CLI

The [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) allows us to invoke given handler locally utilizing a _[Docker](https://www.docker.com/) container_. If we decide on developing a custom _AWS Lambda runtime_ locally using [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification.html), all we have to do is adhere to the _AWS Lambda Runtime API_ specification and write a couple of lines of YAML.

Let us start with a AWS SAM template.

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Description: An example serverless API and worker written in Golang.

Resources:
  Hello:
    Type: AWS::Serverless::Function
    Properties:
      # The custom runtime will be written in Go, thus the `provided.al2` runtime.
      Runtime: provided.al2
      Handler: bootstrap
      CodeUri: ./
```

Next the runtime itself. For the sake of the example, the _runtime loop_ was not implemented.

```go
package main

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"os"
)

// Huge simplification. Should not be used in production setting in any shape or form.
func main() {
	// As per documentation: https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
	baseUrl := fmt.Sprintf("http://%v/2018-06-01/runtime/invocation", os.Getenv("AWS_LAMBDA_RUNTIME_API"))
	res, err := http.Get(baseUrl + "/next")
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	reqId := res.Header.Get("Lambda-Runtime-Aws-Request-Id")

	out := handler(context.TODO())
	_, err = http.Post(fmt.Sprintf("%v/%v/response", baseUrl, reqId), "text/plain", bytes.NewReader([]byte(out)))
	if err != nil {
		panic(err)
	}
}

// Your deployed code.
func handler(ctx context.Context) string {
	return "Hello"
}
```

The code follows the [_AWS Lambda runtime API_ documentation](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html).

Before we invoke our custom runtime, we have to compile it.

```shell
GOARCH=amd64 GOOS=linux go build -o ./bootstrap bootstrap.go
```

Please note that in the context of an actual deployment, the name of the binary does matter. I **did not** pick `boostrap` by random.
For additional resources regarding the naming, consult the _Using custom runtime_ section of the [revenant documentation](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html).

All that is left for us to do with the binary created is to test our code. To do so, let us use the `sam local invoke` command.

```shell
sam local invoke --no-event Hello
```

If you followed the steps carefully, the result of the invocation should be a plain string - `Hello`.

#### Using an emulator

Enter the [aws-lambda-runtime-interface-emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator). This tool will allow us to emulate the _AWS Lambda Runtime API_ locally (I suspect that the AWS SAM uses this tool under the hood as well). The `aws-lambda-runtime-interface-emulator` is designed to be used with [_Docker_](https://www.docker.com/). Still, nothing stops us from containerizing our code for the sake of development and then, whenever we are ready, proceeding with deployment how we wish to. This local workflow is a bit more involved than the previous one, but it might be a valid alternative for those not using AWS SAM or are already using containers to deploy their Lambdas.

A great article already exists on setting up the `aws-lambda-runtime-interface-emulator` on the [official AWS documentation site](https://docs.aws.amazon.com/lambda/latest/dg/go-image.html). The code that we have written before is fully compatible with the guide. If you want to give it a try, make sure not to skip the _To add RIE to the image_ section.

## Summary

I hope this journey into the _AWS Lambda Runtime API_ was just as educating for you as it was for me. While I do not think that the knowledge presented here is necessary to develop _AWS Lambda_ based applications effectively, I believe that there might be occasions when it's useful.
Weird edge cases happen, and knowing how the runtime of the language of your choice works might help you debug them more effectively.

Please do not hesitate to reach out. You can find me on twitter - [@wm_matuszewski](https://twitter.com/wm_matuszewski)

Thank you for your time.