Use Lambda Function URL to write a Serverless app backed by DynamoDB
Tags: tutorial, serverless, programming, devops
Published: 2022-05-10 03:19:15

*This is a Go Lambda function that's packaged and deployed using AWS SAM*

[Lambda Function URL](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html) is a relatively new feature (at the time of writing this blog) that provides dedicated HTTP(S) endpoint for your Lambda function. It is really useful when all you need is a single endpoint for your function (e.g. to serve as a webhook) and don't want to setup and configure an API Gateway. Looks like I can't seem to get enough of it! I have written a couple of blog posts on this topic that include a practical example of using it to [build a serverless backend](https://dev.to/aws/using-aws-lambda-function-url-to-build-a-serverless-backend-for-slack-3j73) and then [deploying that solution using AWS CDK](https://dev.to/aws/package-and-deploy-a-lambda-function-as-a-docker-container-with-aws-cdk-4hp4).

This is yet another blog post (it's a short one!) that demonstrates how you can use Lambda Function URL to write a simple application backed by DynamoDB. You will be able to invoke an API endpoint exposed by the Lambda Function URL,which in turn will execute operations (`GetItem`, `PutItem`, `Scan`) on DynamoDB. The function is written in Go using the [DynamoDB package](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/dynamodb) in [AWS Go SDK](https://aws.amazon.com/sdk-for-go/) and [AWS Serverless Application Model (SAM)](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) is used to quickly build and deploy the solution.

> The code is [available on GitHub](https://github.com/abhirockzz/lambda-functionurl-dynamodb-sam-go) for your reference

Let's get right to it! Before you move on, make sure you have the following ready:

## Pre-requisites

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Go](https://go.dev/dl/) (`1.16` or above) installed
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)

## Deployment

Start by cloning the Github repo and change to the right directory:

``` bash
git clone https://github.com/abhirockzz/lambda-functionurl-dynamodb-sam-go
cd lambda-functionurl-dynamodb-sam-go
```

Use AWS SAM to build the app:

```bash
sam build
```

Now you're ready to deploy the application:

```bash
sam deploy --guided
```

When prompted, enter the required information such as stack name etc. See example below:

```
Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: 
        AWS Region [us-east-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: y
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]: n
        DemoFunction Function Url may not have authorization defined, Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: 
        SAM configuration file [samconfig.toml]: 
        SAM configuration environment [default]: 
```

> Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults.

If successful, you should now have the following ready:

- Lambda Function with a HTTP(S) URL
- A DynamoDB table (called `users`)
- IAM Role with minimum required permissions for Lambda as well as DynamoDB (`PutItem`, `GetItem`, and `Scan`)

> Note the output from the SAM deployment process. This contains the HTTP URL endpoint for your function required for testing

## Test the application

Testing the integration involves sending an HTTP requests to the Lambda Function URL. This example used the [curl](https://curl.se/) CLI, but you can also use other options.

Export the Lambda Function URL endpoint as an environment variable.

```bash
export LAMBDA_FUNCTION_URL_ENDPOINT=<SAM deployment output>
e.g. export LAMBDA_FUNCTION_URL_ENDPOINT=https://qfmpu2n74ik7m2m3ipv3m6yw5a0qiwmp.lambda-url.us-east-1.on.aws/
```

Invoke the endpoint to create new entries in the DynamoDB table:

```bash
curl -i -X POST -H "Content-Type: application/json" -d '{"email":"user1@foo.com", "username":"user-1"}' $LAMBDA_FUNCTION_URL_ENDPOINT

curl -i -X POST -H "Content-Type: application/json" -d '{"email":"user2@foo.com", "username":"user-2"}' $LAMBDA_FUNCTION_URL_ENDPOINT
```

You should get an HTTP `201 Created` response in both the cases. This indicates that the items have been added to the `users` table in DynamoDB.

Let's retrieve the info for the record we just created (we do that by adding a `query` parameter (email) to the URL):

```bash
curl -i $LAMBDA_FUNCTION_URL_ENDPOINT?email=user2@foo.com
```

To retrieve all the items that you just added:

```bash
curl -i $LAMBDA_FUNCTION_URL_ENDPOINT
```

Try to search for an item that you haven't yet added:

```bash
curl -i $LAMBDA_FUNCTION_URL_ENDPOINT?email=notthere@foo.com
```

You will get back a `HTTP 404 Not Found` response in this case.

Finally, try to insert a duplicate record:

```bash
curl -i -X POST -d '{"email":"user2@foo.com", "username":"user-2"}' $LAMBDA_FUNCTION_URL_ENDPOINT
```

The Lambda function returns an HTTP `409 Conflict` in this case since a [Condition Expression](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ConditionExpressions.html) (`attribute_not_exists(email)`) prevents an existing item (with the same `email`) from being overwritten by the `PutItem` call.

That's it! You have tried all the operations exposed by the Lambda function.

## Cleanup
 
Once you're done, please delete the stack:

```bash
sam delete --stack-name STACK_NAME
```

Confirm the stack has been deleted
    
```bash
aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus"
```

## Wrap up!

This was a short (but hopefully useful) tutorial! You can use this to quickly bootstrap a Serverless app with a DynamoDB backend with functionality exposed by a Lambda Function URL.

Happy coding!