How to Deploy Dart Functions to AWS Lambda
Tags: awslambda, aws, dart, flutter
Published: 2024-05-08 07:35:25

## Introduction

While [AWS Lambda](https://aws.amazon.com/lambda/), a highly popular serverless computing service, does not officially provide a [Dart](https://dart.dev) runtime, you can deploy functions created in [Dart](https://dart.dev) to [AWS Lambda](https://aws.amazon.com/lambda/) using the custom runtime mechanism.

Deploying [Dart](https://dart.dev) functions to [AWS Lambda](https://aws.amazon.com/lambda/) enables you to utilize them not only within AWS Lambda but also integrate them with services like [Amazon API Gateway](https://aws.amazon.com/api-gateway/), allowing you to leverage them in [Flutter](https://flutter.dev) applications as well. This unified codebase in [Dart](https://dart.dev) offers great convenience.

In this article, we will briefly introduce how to deploy [Dart](https://dart.dev) functions to [AWS Lambda](https://aws.amazon.com/lambda/).

## Preparation

In this article, we will use [Serverless Framework](https://www.serverless.com/) and [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy Dart functions to AWS Lambda. If you haven't installed them yet, please complete the installation using the following commands or links.

Additionally, you'll need an AWS account and access tokens for AWS services.

### AWS Account

https://aws.amazon.com

### AWS Authentication Token

Once you have generated the authentication token, set it in the environment variables as follows:

```bash
export AWS_ACCESS_KEY_ID=xxxxxxxxxx
export AWS_SECRET_ACCESS_KEY=xxxxxxxxxx
```

### Serverless Framework

```bash
brew install serverless
```

https://www.serverless.com/

### Docker Desktop

https://www.docker.com/products/docker-desktop/

## Implementation and Registration of Functions

Now let's implement the Dart function to deploy it to AWS Lambda.

### Project Structure

We'll proceed with the following structure. Details about `Dockerfile` and `serverless.yml` will be explained later.

```bash
.
├── Dockerfile
├── LICENSE
├── analysis_options.yaml
├── lib
│   ├── functions.dart
│   └── main.dart
├── pubspec.lock
├── pubspec.yaml
└── serverless.yml
```

### Installing Packages

When deploying to AWS Lambda, you'll need to use the Dart custom runtime [aws_lambda_dart_runtime_ns](https://pub.dev/packages/aws_lambda_dart_runtime_ns). Please add it to your dependencies in `pubspec.yaml` using the following command or manually:

https://pub.dev/packages/aws_lambda_dart_runtime_ns

```bash
dart pub add aws_lambda_dart_runtime_ns
```

```bash
dart pub get
```

```yaml
name: sample

environment:
  sdk: ^3.3.4

dependencies:
  aws_lambda_dart_runtime_ns: ^0.0.13

dev_dependencies:
  lints: ^3.0.0
  test: ^1.24.0
```

### Creating a Function to Deploy to AWS Lambda

Let's create a simple function to deploy to AWS Lambda.
For now, let's make the function simply return the string `Hello, World!` when called.

```dart
import 'package:aws_lambda_dart_runtime_ns/aws_lambda_dart_runtime_ns.dart';

FunctionHandler get sayHelloWorld {
  return FunctionHandler(
    name: 'hello-world',
    action: (context, event) {
      // Perform various operations here such as 
      // file IO, database operations, etc.

      return InvocationResult(
        requestId: context.requestId,
        body: {
          'message': 'Hello, World!',
        },
      );
    },
  );
}
```

To briefly explain, the `FunctionHandler` object represents the function executed on AWS Lambda.

The `name` of `FunctionHandler` is the required name when the function (or command) is invoked.
Additionally, this name must be associated with the command definition in `serverless.yml`, which will be introduced later.

The `action` of `FunctionHandler` defines the actual behavior of the function (or command). In this example, it simply returns the message `'Hello, World!'`, but you can perform various operations such as file operations or DB operations within `action`.

The `InvocationResult` needed to be returned in `action` represents the result of the processing. You can set any JSON data in the `body` of `InvocationResult`.

### Registering the Created Function to the Custom Runtime

Let's register the `sayHelloWorld` function we created earlier to the custom runtime. It's very simple.

```dart
import 'package:aws_lambda_dart_runtime_ns/aws_lambda_dart_runtime_ns.dart';
import 'package:sample/functions.dart' as fn;

Future<void> main(List<String> args) async {
  await invokeAwsLambdaRuntime([
    fn.sayHelloWorld, // This is what you need to do
  ]);
}
```

When registering the created function to the custom runtime, all you need to do is call the `invokeAwsLambdaRuntime` function and pass the functions you want to register as arguments.

With this, the process of creating the function you want to register with AWS Lambda and registering it with the custom runtime is complete.

## Deploying to AWS Lambda

Once you've reached this point, all that's left is to deploy the program you've created to AWS Lambda.
We'll define the `Dockerfile` and `serverless.yml` as follows:

I'm not very knowledgeable about hardware, but it seems that using ARM-based [AWS Graviton](https://aws.amazon.com/blogs/aws/aws-lambda-functions-powered-by-aws-graviton2-processor-run-your-functions-on-arm-and-get-up-to-34-better-price-performance/) makes you happy, so we'll proceed with an architecture based on arm64.

### Dockerfile

```docker
FROM arm64v8/dart:latest as build-image

WORKDIR /work
COPY ./ ./

RUN dart pub get
RUN dart compile exe ./lib/main.dart -o ./bootstrap
RUN chmod +x bootstrap

FROM --platform=linux/arm64 public.ecr.aws/lambda/provided:latest

COPY --from=build-image /work/bootstrap /var/runtime/

CMD ["dummyHandler"]
```

If the folder structure where the Dart program is located is different, please modify the `./lib/main.dart` part accordingly.

### serverless.yml

```yml
service: sample-app

provider:
  name: aws
  runtime: provided
  timeout: 30
  region: ap-northeast-1
  architecture: arm64
  ecr:
    images:
      appImage:
        path: ./
        platform: linux/arm64

functions:
  hello-world:
    image:
      name: appImage
      command:
        - hello-world
```

Since there is no specific preference for the region, let's use `ap-northeast-1 (Tokyo)` for now.

When defining `functions`, please pay attention to ensuring that the name of the `command` matches the `name` defined earlier for `FunctionHandler`.

### Deployment

Once you have configured everything, you can deploy the function you created to AWS Lambda using the following Serverless Framework command:

Please make sure Docker Desktop is running before executing the command.

```bash
sls deploy
```

```bash
sls deploy
Running "serverless" from node_modules
(node:6460) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)

Deploying sample-app to stage dev (ap-northeast-1)

✔ Service deployed to stack sample-app-dev (32s)

functions:
  hello-world: sample-app-dev-hello-world
```

## References

The project created in this article is publicly available in the following repository. If you'd like to quickly try it out, feel free to clone or fork it and deploy it to AWS Lambda to play around with.

https://github.com/myConsciousness/sample-aws-lambda-with-dart-runtime