Dart VM on AWS Lambda

05/25/2019

Dart VM on AWS Lambda

I’ve been getting into Flutter and of course leaning Dart in the process. I didn’t know a thing about Dart when I started, not even the name. I won’t get into a history lesson you can check out what Wikipedia or Dart’s official website have to say about the language.

Long story short, I’ve really enjoyed learning and writing Dart. I’ve quickly fallen in love with Flutter too, in part because of Dart. I love that Dart is a multi-paradigm runtime. JIT (Just In Time) compiled and AOT (Ahead Of Time) compiled. It allows the best of both worlds! JIT is fantastic while developing giving you quick feedback with sub-second UI updates and when you are ready to ship your code utilize the benefits of AOT speed.

Serverless and JavaScript

I’m also a big fan of serverless and run a number of mobile apps using Amazon’s API Gateway and Lambda. JavaScript and NodeJS are incredibly popular and I currently use NodeJS with AWS Lambda. It is incredibly appealing to learn and write a single language to run across multiple platforms.

The JS But

JavaScript is great, until it isn’t. JavaScript has many great use cases but building large scale applications isn’t one of them, particularly mobile apps. Yes, testing helps mitigate that but at the end of the day, I’m not a fan of JavaScript used by monoliths, at scale without a type system. If you don’t keep a close eye on your mobile app it can quickly turn into a very coupled, monolith! That said, I do think JavaScript/NodeJS is a good candidate for cloud functions. But, this post is about Dart!

Serverless and Dart VM

Before Flutter and Dart, JavaScript seemed to be the only option that covered mobile, web and server. After using Flutter on mobile and the web, I figured I’d give it a shot on the server too, so why not AWS Lambda!?

I started researching AWS Lambda custom runtime API and looked for other examples. As a Swift fan, I stumbled upon an aws-lambda-swift repository. Shout out to tonisuter! I ported his work over to a Dart implementation. I decided to start simple, so I wanted to run a function using the Dart VM (JIT). Dart AOT is for another post!

The AWS blog shows an example using PHP. It directs you to run an EC2 instance and compile the PHP binary on that machine since it is the Lambda Execution Environment that is used by Lambda. I started down this path to with Dart but ended up taking a different route.

Running Dart VM in AWS Lambda

  1. Instead of building the binary myself I grabbed Google’s docker container, docker pull google/dart, and copied the dart binary out of it. This binary needs to be in the bin/ folder that you will later zip and upload to AWS. UPDATE: Better yet, download the SDK archive from dart.dev
  2. I created my bootstrap file as required by AWS Custom Runtime. This file is nothing too exciting. It is a small script that kicks off the Dart code. Be sure to chmod +x it before uploading.
     #!/bin/sh
     ./bin/dart main.dart
    
  3. Custom runtime requires a few simple API calls to AWS to facilitate. I followed tonisuter’s lead and re-implemented aws-lambda-swift’s Runtime class in Dart. TLDR, an API call is made to AWS to a) get the next invocation b) post a successful response or c) post an error response.
  4. Make your function! I kept it simple for now just taking a number squaring it, and returning the result. You can have a look at the Runtime code on my Gitlab. It’s as straight forward as it looks. Create an instance of Runtime register the function by then name 'squareNumber' and pass the function to the runtime. NOTE: The function name must match the Handler name you provide AWS. In this example, Handler: main.squareNumber.
     // main.dart
     import 'runtime.dart';
    
     void main() async {
         await Runtime()
         ..registerLambda('squareNumber', squareNumber)
         ..start();
     }
    
     Map<String, dynamic> squareNumber(Map<String, dynamic> event, Context context) {
         final squaredNumber = event['number'] * event['number'];
         return {
             'result': squaredNumber
         };
     }
    
  5. Now that we have our code lets ship it to AWS. zip -r dart.zip bin/ bootstrap main.dart runtime.dart
  6. Go to the Lambda console and create a function with custom runtime. Upload dart.zip, again be sure to enter the Handler name as main.squareNumber.
  7. Try it out! Configure a test event:
     {
         "number": 5
     }
    
  8. Run the Test and ta-da!!!
     {
         "result": 25
     }
    

Conclusion

One downside to using the Dart VM instead of NodeJS is that it is a bit heavier on memory usage. This same function for NodeJS would use ~75MB of memory while the Dart VM uses ~150-175MB. For this reason, I had to bump the memory from 128MB Lambda to 192MB.

Overall, writing and running Dart code for web, mobile, and serverless Dart as a backend wins out for me! I don’t have a full-stack Dart project as of this post however I’m looking forward to giving it a shot on my next project. Finally an alternative full-stack language to JavaScript!

Be sure to check out the code on Gitlab. PR’s are always welcome!