<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Akkoro</title>
    <description>The latest articles on Forem by Akkoro (@akkoro).</description>
    <link>https://forem.com/akkoro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3841%2F5f66ca24-cf16-4962-8825-16a5e3ff5016.png</url>
      <title>Forem: Akkoro</title>
      <link>https://forem.com/akkoro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/akkoro"/>
    <language>en</language>
    <item>
      <title>A full-stack serverless application with AssemblyLift and Next.js</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Tue, 11 Oct 2022 19:21:45 +0000</pubDate>
      <link>https://forem.com/akkoro/a-full-stack-serverless-application-with-assemblylift-and-nextjs-197g</link>
      <guid>https://forem.com/akkoro/a-full-stack-serverless-application-with-assemblylift-and-nextjs-197g</guid>
      <description>&lt;p&gt;Today we published a new demo illustrating several features of the latest AssemblyLift release!&lt;/p&gt;

&lt;p&gt;This demo deploys a simple "hello world" Next.js application to an AWS environment. Visits to the app are counted by invoking a logging function from the server function.&lt;/p&gt;

&lt;p&gt;The demo repository is &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws"&gt;on GitHub&lt;/a&gt;. You can see it live &lt;a href="https://nextjs.demos.asml.akkoro.io/"&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Topics illustrated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serving static web assets from an AssemblyLift function&lt;/li&gt;
&lt;li&gt;Serving a private API from an AssemblyLift function&lt;/li&gt;
&lt;li&gt;Authoring AssemblyLift functions in both Rust and Ruby&lt;/li&gt;
&lt;li&gt;Sandboxed WebAssembly network access using an IO Module

&lt;ul&gt;
&lt;li&gt;Making HTTP calls to another function or to a 3rd party service&lt;/li&gt;
&lt;li&gt;Making calls to an AWS service (Secrets Manager)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Deploying a service to a custom domain name&lt;/li&gt;
&lt;li&gt;Automated TLS certificate configuration &amp;amp; provisioning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/akkoro/assemblylift"&gt;AssemblyLift&lt;/a&gt; is an open platform for cloud-native application development. AssemblyLift provides a portable, function-oriented framework and WebAssembly-based runtime which can be deployed to AWS Lambda or Kubernetes. The AssemblyLift CLI generates &lt;a href="https://terraform.io"&gt;HashiCorp Terraform&lt;/a&gt; infrastructure code from simple TOML definitions, and takes care of compiling and packaging functions and services for deployment. To make a clichéd comparison, think of it as &lt;em&gt;Infrastructure on Rails&lt;/em&gt; 😛&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org"&gt;Next.js&lt;/a&gt; by &lt;a href="https://vercel.com"&gt;Vercel&lt;/a&gt; is a popular JavaScript/TypeScript frontend framework based on &lt;a href="http://reactjs.org"&gt;React&lt;/a&gt;. Next allows us to export our React application as static HTML which we can serve with AssemblyLift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Services &amp;amp; Functions
&lt;/h3&gt;

&lt;p&gt;AssemblyLift applications are composed of &lt;strong&gt;services&lt;/strong&gt;, each of which are made up of &lt;strong&gt;functions&lt;/strong&gt;. Services are stored in the &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/"&gt;&lt;code&gt;services&lt;/code&gt;&lt;/a&gt; directory. Each service is made up of a &lt;strong&gt;service manifest&lt;/strong&gt; named &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/www/service.toml"&gt;&lt;code&gt;service.toml&lt;/code&gt;&lt;/a&gt; alongside one or more functions. This demo application deploys one service named &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/www/"&gt;&lt;code&gt;www&lt;/code&gt;&lt;/a&gt; which contains two functions, &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/www/server/"&gt;&lt;code&gt;server&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/www/counter/"&gt;&lt;code&gt;counter&lt;/code&gt;&lt;/a&gt;, each of which do exactly what it says on the tin :).  &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;server&lt;/code&gt; function is our HTTP server which serves web content. The &lt;code&gt;counter&lt;/code&gt; function is a private function (protected by IAM) which is called from &lt;code&gt;server&lt;/code&gt;; this function updates a simple count of visits by IP in a &lt;a href="https://xata.io"&gt;Xata&lt;/a&gt; database.  &lt;/p&gt;

&lt;p&gt;For performance, the &lt;code&gt;server&lt;/code&gt; function is written in &lt;a href="https://rust-lang.org"&gt;Rust&lt;/a&gt;. Rust compiles natively to WebAssembly, and so has faster cold-start time as well as faster execution speed compared to Ruby -- ideal for our server! We leverage a Rust crate called &lt;code&gt;rust_embed&lt;/code&gt; which allows us to embed assets inside a compiled binary. We use this to embed the assets generated by our Next.js build inside the WebAssembly module which AssemblyLift will deploy as our Lambda function!  &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;counter&lt;/code&gt; function is written in &lt;a href="https://ruby-lang.org"&gt;Ruby&lt;/a&gt;. Since Ruby is an interpreted language, AssemblyLift deploys a customized Ruby 3.1 interpreter compiled to WebAssembly, which executes the function handler. Since the interpreter is somewhat large, the cold-start time of a Ruby function tends to be larger than that of a Rust function. Our counter is being run in the backround, so we're fine with it being a little bit laggy at times 😉.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP API
&lt;/h3&gt;

&lt;p&gt;Regardless of infrastructure provider, AssemblyLift services are placed behind some kind of API Gateway service. When deployed to AWS, each AssemblyLift service is placed behind an an&lt;a href="https://aws.amazon.com/api-gateway/"&gt; Amazon API Gateway&lt;/a&gt; endpoint. Each function may define an HTTP route which will invoke it. The &lt;code&gt;server&lt;/code&gt; function is invoked by &lt;code&gt;GET /{path+}&lt;/code&gt;; the &lt;code&gt;{path+}&lt;/code&gt; token is a path parameter where the &lt;code&gt;+&lt;/code&gt; indicates that it is a greedy parameter. This means that everything after the first &lt;code&gt;/&lt;/code&gt; is mapped to a parameter called &lt;code&gt;path&lt;/code&gt;. This allows us to map the requested path to an embedded path in our function binary. The &lt;code&gt;counter&lt;/code&gt; function is invoked by &lt;code&gt;POST /api/counter/{ip}&lt;/code&gt;, where &lt;code&gt;{ip}&lt;/code&gt; is a regular path parameter named &lt;code&gt;ip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A function's HTTP route is defined in &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/www/service.toml#L27"&gt;&lt;code&gt;service.toml&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[api.functions]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"counter"&lt;/span&gt;
&lt;span class="py"&gt;language&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ruby"&lt;/span&gt;
&lt;span class="py"&gt;size_mb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3584&lt;/span&gt;
&lt;span class="nn"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;verb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/api/counter/{ip}"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# This defines the HTTP route for counter&lt;/span&gt;
&lt;span class="py"&gt;authorizer_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"iam"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;counter&lt;/code&gt; function is protected by API Gateway's built-in IAM authorizer. AssemblyLift doesn't yet support defining access permissions between functions in TOML, so for this demo a small amount of Terraform is included to attach IAM policies to each function. AssemblyLift instantiates the &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/user_tf/"&gt;&lt;code&gt;user_tf&lt;/code&gt;&lt;/a&gt; directory (if found) as a module alongside its generated module(s).&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain name mapping
&lt;/h3&gt;

&lt;p&gt;AssemblyLift projects can specify one or more domain names to which services can be mapped using a DNS provider. At the moment, the only available DNS provider is &lt;a href="https://aws.amazon.com/route53/"&gt;Amazon Route53&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Domain names are defined as an array in &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/assemblylift.toml#L7"&gt;&lt;code&gt;assemblylift.toml&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[domains]]&lt;/span&gt;
&lt;span class="py"&gt;dns_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"demos.asml.akkoro.io"&lt;/span&gt;
&lt;span class="nn"&gt;[domains.provider]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"route53"&lt;/span&gt;
&lt;span class="nn"&gt;[domains.provider.options]&lt;/span&gt;
&lt;span class="py"&gt;aws_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"us-east-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Route53, the &lt;code&gt;dns_name&lt;/code&gt; must correspond to an &lt;em&gt;existing&lt;/em&gt; Route53 Hosted Zone.&lt;/p&gt;

&lt;p&gt;By default, services are mapped to subdomains according to the pattern &lt;code&gt;service-name.project-name.domain-name.tld&lt;/code&gt;. For example, the &lt;code&gt;www&lt;/code&gt; service would be &lt;code&gt;www.nextjs.demos.asml.akkoro.io&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A service can indicate that it is the &lt;em&gt;root&lt;/em&gt; service, omitting the service subdomain from the path. For example in &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/www/service.toml#L12"&gt;&lt;code&gt;service.toml&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[api]&lt;/span&gt;
&lt;span class="py"&gt;domain_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"demos.asml.akkoro.io"&lt;/span&gt;
&lt;span class="py"&gt;is_root&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;yields the domain &lt;code&gt;nextjs.demos.asml.akkoro.io&lt;/code&gt; for the &lt;code&gt;www&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;When deployed to AWS, AssemblyLift will provision TLS certificates for your services using &lt;a href="https://aws.amazon.com/certificate-manager/"&gt;Amazon Certificate Manager (ACM)&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  IO Modules
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;counter&lt;/code&gt; function uses a Xata database for storage, which provides a JSON-oriented HTTP API which we can access using the HTTP &lt;em&gt;IO Module&lt;/em&gt;. WebAssembly's sandboxing means that by default, functions cannot communicate over a socket or access the filesystem. AssemblyLift provides an RPC interface allowing communication with services called &lt;a href="https://docs.assemblylift.akkoro.io/learn-assemblylift/io-modules"&gt;IO Modules&lt;/a&gt; or &lt;em&gt;IOmods&lt;/em&gt;. IOmods are deployed per-service, i.e. every function in a service share the same set of IOmods. An IOmod is essentially a library of remote functions, each at an assigned &lt;em&gt;coordinate&lt;/em&gt; &lt;code&gt;organization.namespace.module.call&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, importing the standard HTTP module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[iomod.dependencies]]&lt;/span&gt;
&lt;span class="py"&gt;coordinates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"akkoro.std.http"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3.0"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"registry"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;exports a single function named &lt;code&gt;request&lt;/code&gt; at &lt;code&gt;akkoro.std.http.request&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Frontend
&lt;/h3&gt;

&lt;p&gt;There is no specific convention for adding a frontend to AssemblyLift; in this project we have created the &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/frontend/"&gt;&lt;code&gt;frontend&lt;/code&gt;&lt;/a&gt; directory to mimic the &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/services/"&gt;&lt;code&gt;services&lt;/code&gt;&lt;/a&gt; directory. Here we have used &lt;code&gt;create-next-app&lt;/code&gt; to create a Next.js project called &lt;a href="https://github.com/akkoro/assemblylift-demo-nextjs-aws/blob/main/frontend/www/"&gt;&lt;code&gt;www&lt;/code&gt;&lt;/a&gt; after our service of the same name.&lt;/p&gt;

&lt;p&gt;The frontend must be exported to a static site, using &lt;code&gt;next build &amp;amp;&amp;amp; next export&lt;/code&gt;. The contents of the generated &lt;code&gt;out&lt;/code&gt; directory are copied into the server binary when it is compiled (on &lt;code&gt;asml cast&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your own!
&lt;/h2&gt;

&lt;p&gt;You can deploy this project yourself! You will need to update it to use your own domain of course ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  You will need...
&lt;/h3&gt;

&lt;p&gt;Required: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account&lt;/li&gt;
&lt;li&gt;Rust &amp;amp; Cargo CLI&lt;/li&gt;
&lt;li&gt;Node &amp;amp; NPM CLI&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/akkoro/assemblylift/releases"&gt;Latest AssemblyLift CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nice to have: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A domain name in Route53&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An AssemblyLift project is built with the &lt;code&gt;asml cast&lt;/code&gt; command; &lt;em&gt;casting&lt;/em&gt; is the process of compiling WASM, transpiling TOML, building images, etc which comprises the build of an application. Artifacts and plans are serialized to the &lt;code&gt;net/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;To deploy a project you use the command &lt;code&gt;asml bind&lt;/code&gt;, which will deploy artifacts and apply the underlying Terraform plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reach out!
&lt;/h2&gt;

&lt;p&gt;Get in touch with us &lt;a href="https://discord.gg/pVSCqYgra3"&gt;on Discord&lt;/a&gt; and follow &lt;a href="https://twitter.com/akkorocorp/"&gt;@akkorocorp&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cloud</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AssemblyLift alpha latest: easy API Gateway for Kubernetes functions, Ruby language support 💎</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Wed, 22 Jun 2022 16:41:59 +0000</pubDate>
      <link>https://forem.com/akkoro/assemblylift-alpha-latest-easy-api-gateway-for-kubernetes-functions-ruby-language-support-1045</link>
      <guid>https://forem.com/akkoro/assemblylift-alpha-latest-easy-api-gateway-for-kubernetes-functions-ruby-language-support-1045</guid>
      <description>&lt;p&gt;Hello again y'all! 👋🏻&lt;/p&gt;

&lt;p&gt;Since our &lt;a href="https://dev.to/akkoro/assemblylift-v040-alpha-released-with-kubernetes-support-wasi-webassembly-on-k8s-5bj8"&gt;last post introducing the AssemblyLift v0.4-alpha series&lt;/a&gt;, we've had two new alpha &lt;a href="https://github.com/akkoro/assemblylift/releases"&gt;releases&lt;/a&gt; which introduced some major additions &amp;amp; improvements!&lt;/p&gt;

&lt;p&gt;In this post we'll talk about each change and what it means, and then we'll walk through a short example demonstrating the  new features. 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Revised TOML manifest spec
&lt;/h2&gt;

&lt;p&gt;Manifests (&lt;code&gt;assemblylift.toml&lt;/code&gt; and &lt;code&gt;service.toml&lt;/code&gt;) now use arrays to describe most entries which used to be written using tables.&lt;/p&gt;

&lt;p&gt;For example, the &lt;code&gt;[services]&lt;/code&gt; table in &lt;code&gt;assemblylift.toml&lt;/code&gt; is now the &lt;code&gt;[[services]]&lt;/code&gt; array.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;The primary data structures in &lt;a href="https://toml.io/en/"&gt;TOML&lt;/a&gt; are arrays and tables.&lt;/p&gt;

&lt;p&gt;Previous releases leaned heavily on &lt;em&gt;tables&lt;/em&gt;. A typical &lt;code&gt;assemblylift.toml&lt;/code&gt; for example might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-project"&lt;/span&gt;

&lt;span class="nn"&gt;[services]&lt;/span&gt;
&lt;span class="nn"&gt;my_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-service"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nn"&gt;another&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"another-service"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both &lt;code&gt;[project]&lt;/code&gt; and &lt;code&gt;[services]&lt;/code&gt; are tables. The &lt;code&gt;my_service&lt;/code&gt; key also points to a table. Expressing services this way (as well as functions, authorizers, etc.) is a bit verbose considering that these things are also named.&lt;/p&gt;

&lt;p&gt;By comparison, using arrays to add multiple services to &lt;code&gt;assemblylift.toml&lt;/code&gt; for example looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"project"&lt;/span&gt;

&lt;span class="nn"&gt;[[services]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"service1"&lt;/span&gt;

&lt;span class="nn"&gt;[[services]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"service2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which avoids redundant information (the table key) and becomes easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruby language support
&lt;/h2&gt;

&lt;p&gt;AssemblyLift is now bilingual! Functions can now be written in Ruby in addition to Rust.&lt;/p&gt;

&lt;p&gt;To declare a function a Ruby function, set the &lt;code&gt;language&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[api.functions]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-ruby-func"&lt;/span&gt;
&lt;span class="py"&gt;language&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ruby"&lt;/span&gt;
&lt;span class="nn"&gt;http&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;verb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/my-service/my-ruby-func"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Ruby function requires &lt;code&gt;handler.rb&lt;/code&gt; to be the entrypoint. AssemblyLift ships a custom build of the Ruby runtime which includes a module for interop with the AssemblyLift platform.&lt;/p&gt;

&lt;p&gt;We'll explore this in more detail in the demo section below!&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;We ❤️ Ruby! Most commonly associated with the Rails framework, Ruby is a powerful &amp;amp; mature scripting language suitable for many purposes. &lt;/p&gt;

&lt;p&gt;Ruby recently gained support for WebAssembly, and we almost immediately went about integrating it into AssemblyLift! 💨&lt;/p&gt;

&lt;p&gt;While you can't (yet?) lift your existing Rails app to a Function, the Ruby language is familiar to many devs. Ruby could especially be preferable in cases where performance is less of a concern, such as automation tasks for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for Gloo Edge API Gateway
&lt;/h2&gt;

&lt;p&gt;Another feature addition we're very excited about is HTTP API support for the new Kubernetes provider. The previous alpha release could deploy functions to a K8s cluster, but there was no practical way yet to invoke them!&lt;/p&gt;

&lt;p&gt;This release will automatically install &lt;a href="https://docs.solo.io/gloo-edge/latest/introduction/"&gt;Gloo Edge&lt;/a&gt; on the target cluster. AssemblyLift will deploy a Gateway, and set up routing for any functions running in a K8s service which define an HTTP API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;An API Gateway is central to the AssemblyLift architecture. AWS Lambda has a natural pairing with Amazon API Gateway, but Kubernetes of course is open-ended.&lt;/p&gt;

&lt;p&gt;For this first iteration, Gloo Edge was chosen as it is open-source, easy to automate, and very full-featured. Being itself based on &lt;a href="https://www.envoyproxy.io/"&gt;Envoy Proxy&lt;/a&gt; we can eventually explore its support for WASM traffic filters in Gloo Edge Enterprise.&lt;/p&gt;

&lt;p&gt;A potential drawback is that Gloo may be heavier than needed for your application (our test deployment required 2vCPU per node to run Gloo + an AssemblyLift service). We'll soon look at support for other (lighter) reverse proxies. Eventually we want to support multiple API Gateway providers per backend, and also allow for cross deployment (Amazon APIGW in front of K8s, Gloo in front of Lambda).&lt;/p&gt;

&lt;h2&gt;
  
  
  A short demonstration
&lt;/h2&gt;

&lt;p&gt;Let's look at an example! If you want to try AssemblyLift yourself and follow along, you will need the prerequisites below. Otherwise if you just want the gist of things, you can skip that and keep reading 🙂&lt;/p&gt;

&lt;p&gt;In this example we'll deploy a basic Ruby function to a Kubernetes cluster that writes to the function log, and &lt;code&gt;curl&lt;/code&gt; it via an API Gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prereqs
&lt;/h3&gt;

&lt;p&gt;You will need a running Docker daemon on your development machine. You will also need a configured k8s config at the default &lt;code&gt;~/.kube/config&lt;/code&gt;. So far AssemblyLift has been tested against Docker Desktop Kubernetes as well as Digital Ocean Kubernetes. It isn't strictly necessary, but is recommended for testing and debugging, to have &lt;code&gt;kubectl&lt;/code&gt; installed.&lt;/p&gt;

&lt;p&gt;You will also need a Docker image registry. Right now AssemblyLift supports Docker Hub and AWS ECR, however ECR is recommended as your registries can be provisioned/destroyed automatically. Currently, AssemblyLift will attempt to use AWS credentials from your default profile in &lt;code&gt;~/.aws/credentials&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can grab AssemblyLift for macOS with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; public.assemblylift.akkoro.io/cli/0.4.0-alpha.3/x86_64-apple-darwin/asml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for Linux with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; public.assemblylift.akkoro.io/cli/0.4.0-alpha.3/x86_64-linux-gnu/asml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the binary executable by running &lt;code&gt;chmod +x asml&lt;/code&gt; and ensure it is available on your system &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If your Linux system's glibc isn't compatible with the asml binary you may need to build from source instead. The easiest way to do this is to install with &lt;code&gt;cargo&lt;/code&gt; by running &lt;code&gt;cargo install assemblylift --version 0.4.0-alpha.3&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Ruby function
&lt;/h3&gt;

&lt;p&gt;We'll create a new project with a Ruby template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asml init &lt;span class="nt"&gt;-n&lt;/span&gt; ruby-example &lt;span class="nt"&gt;-l&lt;/span&gt; ruby
&lt;span class="nb"&gt;cd &lt;/span&gt;ruby-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a basic Ruby function called &lt;code&gt;my-function&lt;/code&gt; inside a service &lt;code&gt;my-service&lt;/code&gt;. These can be renamed as you like, however the directory names also currently need to be updated manually to match (we'll have this sync in a future release :)).&lt;/p&gt;

&lt;p&gt;Add an HTTP route definition to the function in &lt;code&gt;service.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[api.functions]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-function"&lt;/span&gt;
&lt;span class="py"&gt;language&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ruby"&lt;/span&gt;
&lt;span class="py"&gt;registry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ecr"&lt;/span&gt; &lt;span class="c"&gt;# or "dockerhub" if using Docker Hub&lt;/span&gt;
&lt;span class="nn"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;verb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ALL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/my-service/my-function"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set provider and configure container registry
&lt;/h3&gt;

&lt;p&gt;Again in &lt;code&gt;service.toml&lt;/code&gt;, add the following to use the Kubernetes backend provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[service.provider]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"k8s"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in &lt;code&gt;assemblylift.toml&lt;/code&gt;, configure the container registry you wish to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[registries]]&lt;/span&gt;
&lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ecr"&lt;/span&gt;
&lt;span class="nn"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;aws_account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"12345890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;aws_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"us-east-1"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[[registries]]&lt;/span&gt;
&lt;span class="py"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dockerhub"&lt;/span&gt;
&lt;span class="nn"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;registry_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;username&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;password&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write some Ruby!
&lt;/h3&gt;

&lt;p&gt;Next, let's open up the function's &lt;code&gt;handler.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'asml'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'base64'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# TODO implement your function code here!&lt;/span&gt;
    &lt;span class="no"&gt;Asml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Asml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_function_input&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;asml&lt;/code&gt; module includes the &lt;code&gt;Asml&lt;/code&gt; class, which exposes basic methods for interacting with the AssemblyLift runtime environment.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; object has shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;method:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;headers:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;body_encoding:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;body:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can decode the body and log it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'body_encoding'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'base64'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="no"&gt;Base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="no"&gt;Asml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="no"&gt;Asml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build, deploy, test!
&lt;/h3&gt;

&lt;p&gt;Generate your deployment by running &lt;code&gt;asml cast&lt;/code&gt;. If there are no errors, you should see a Terraform plan in the output. &lt;/p&gt;

&lt;p&gt;Deploy your service with &lt;code&gt;asml bind&lt;/code&gt;! Once deployed successfully, your cluster should have a Gloo Edge installation in the &lt;code&gt;gloo-system&lt;/code&gt; namespace and a function deployed as &lt;code&gt;ClusterIP&lt;/code&gt;s in an &lt;code&gt;asml-*&lt;/code&gt; namespace for your service (you can check this by running &lt;code&gt;kubectl get namespaces&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The Gloo Edge Gateway will have created a LoadBalancer resource, which should be deployed by your Kubernetes host (e.g. Digital Ocean). You should be able to &lt;code&gt;curl&lt;/code&gt; the LoadBalancer public IP with your function's route!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Hello World"&lt;/span&gt; http://&amp;lt;ip&amp;gt;/my-service/my-function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reach out!
&lt;/h2&gt;

&lt;p&gt;Whether you have questions about using AssemblyLift or just want to chat, you can find us &lt;a href="https://discord.gg/pVSCqYgra3"&gt;on Discord&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cloud</category>
      <category>webassembly</category>
      <category>ruby</category>
    </item>
    <item>
      <title>AssemblyLift v0.4.0-alpha released with Kubernetes support, WASI (WebAssembly on K8s)</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Wed, 27 Apr 2022 18:00:37 +0000</pubDate>
      <link>https://forem.com/akkoro/assemblylift-v040-alpha-released-with-kubernetes-support-wasi-webassembly-on-k8s-5bj8</link>
      <guid>https://forem.com/akkoro/assemblylift-v040-alpha-released-with-kubernetes-support-wasi-webassembly-on-k8s-5bj8</guid>
      <description>&lt;p&gt;Hello devs! 👋🏻 We're back!&lt;/p&gt;

&lt;p&gt;Today we're making available the first alpha release of &lt;a href="https://github.com/akkoro/assemblylift"&gt;AssemblyLift&lt;/a&gt; v0.4. This initial release brings a few exiting improvements to AssemblyLift including&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.wasm.builders/shravi_inamdar/what-is-wasi--4l60"&gt;WASI&lt;/a&gt; modules; Asml functions now target &lt;code&gt;wasm32-wasi&lt;/code&gt; and are compiled as executables instead of libraries&lt;/li&gt;
&lt;li&gt;Simplified Rust function boilerplate (thanks to WASI)&lt;/li&gt;
&lt;li&gt;Experimental Kubernetes backend provider&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hyperium/hyper"&gt;Hyper&lt;/a&gt;-based HTTP function runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look!&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing
&lt;/h2&gt;

&lt;p&gt;We're going to use the new k8s provider, so as a prerequisite you'll need &lt;code&gt;docker&lt;/code&gt; installed on your system as well as a configured Kubernetes cluster. Functions are written in Rust and will need &lt;code&gt;cargo&lt;/code&gt; to be available as well. Finally, as of writing the AssemblyLift runtime images are hosted on public ECR, which &lt;em&gt;may&lt;/em&gt; require authentication using the &lt;code&gt;aws&lt;/code&gt; CLI. &lt;/p&gt;

&lt;p&gt;You can grab AssemblyLift for macOS with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; public.assemblylift.akkoro.io/cli/0.4.0-alpha.1/x86_64-apple-darwin/asml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for Linux with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; public.assemblylift.akkoro.io/cli/0.4.0-alpha.1/x86_64-linux-gnu/asml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can make the binary executable by running &lt;code&gt;chmod +x asml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If necessary, authenticate docker with ECR using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr-public get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; public.ecr.aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy a function to Kubernetes
&lt;/h2&gt;

&lt;p&gt;Let's spin up a new project to experiment with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asml init &lt;span class="nt"&gt;-n&lt;/span&gt; asmlnetes &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;asmlnetes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, open up &lt;code&gt;service.toml&lt;/code&gt; and update our service &amp;amp; function definition to use the k8s backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[service]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-service"&lt;/span&gt;

&lt;span class="nn"&gt;[service.provider]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"k8s-hyper-alpine"&lt;/span&gt;
&lt;span class="nn"&gt;[service.provider.options]&lt;/span&gt;
&lt;span class="py"&gt;registry_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-dockerhub-registry"&lt;/span&gt;
&lt;span class="c"&gt;# ECR is also supported! Instead of registry_name use:&lt;/span&gt;
&lt;span class="c"&gt;# registry_type = "ecr"&lt;/span&gt;
&lt;span class="c"&gt;# aws_account_id = "1234567890"&lt;/span&gt;
&lt;span class="c"&gt;# aws_region = "ca-central-1"&lt;/span&gt;

&lt;span class="nn"&gt;[api.functions.my-function]&lt;/span&gt;
&lt;span class="nn"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"k8s-hyper-alpine"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-function"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've also deployed the HTTP IOmod as a container which can be specified via the &lt;code&gt;iomod&lt;/code&gt; section as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[iomod.dependencies.http]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"container"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2.0"&lt;/span&gt;
&lt;span class="py"&gt;coordinates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"akkoro.std.http"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to play with the HTTP module, you'll need to add the corresponding dependency to the functions' Cargo.toml.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;package&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"assemblylift-iomod-http-guest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our function handler used to be defined in &lt;code&gt;lib.rs&lt;/code&gt;, however with WASI support we now use a regular &lt;code&gt;main&lt;/code&gt; function in &lt;code&gt;main.rs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[handler]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On both Lambda and Kubernetes platforms, function input is now stored in the variable &lt;code&gt;ctx.input&lt;/code&gt;, which is injected by the handler macro.&lt;/p&gt;

&lt;p&gt;The input shape used by the Hyper runtime isn't finalized, but as of writing you deserialize function input as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BTreeMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[handler]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpFunctionRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// The event body is encoded as Z85; in this case we assume it's a string, but it could be binary!&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;str&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;z85&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="py"&gt;.body&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Body = {:?}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpFunctionEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BTreeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IOmods are called as they have been in previous versions, but the new version of the HTTP module guest includes a handy request builder to make things a little easier 🙂&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BTreeMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpRequestBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[handler]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;http_req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HttpRequestBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"akkoro.io"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;http_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HTTP status: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_res&lt;/span&gt;&lt;span class="py"&gt;.code&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nn"&gt;FunctionContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Body = {:?}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_res&lt;/span&gt;&lt;span class="py"&gt;.body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpFunctionEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BTreeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;asml cast&lt;/code&gt; should compile the function and generate a Terraform plan, which if everything is defined correctly should create some Kubernetes resources in a namespace (asml-{project_name}-{service_name}).&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;should&lt;/em&gt; work against whatever cluster is defined in &lt;code&gt;~/.kube/config&lt;/code&gt;, however at the moment only a &lt;code&gt;NodePort&lt;/code&gt; service is created for each function -- &lt;code&gt;LoadBalancer&lt;/code&gt; support for cloud deployments on AKS and such will come in a later update.&lt;/p&gt;

&lt;p&gt;After running &lt;code&gt;asml bind&lt;/code&gt; to deploy your service(s), you'll be able to find the function pods for example with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; asml-asmlnetes-my-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're running on a local K8s cluster (I use the distro provided by Docker Desktop personally), you can cURL requests to &lt;code&gt;localhost&lt;/code&gt; on the exposed NodePorts for each function.&lt;/p&gt;

&lt;p&gt;Find the port by listing the function services&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get services &lt;span class="nt"&gt;-n&lt;/span&gt; asml-asmlnetes-my-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;The 0.4.0-alpha.x series will iterate on the above compute platform enhancements, concentrating mainly on k8s support.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/akkoro/whats-next-for-assemblylift-data-oriented-cloud-dev-through-webassembly-and-capability-based-security-fn3"&gt;data model we wrote about&lt;/a&gt; a while ago is still going through more of a design phase. The plan is to introduce that functionality in a 0.4.0-beta series, and iterate on that up to the final 0.4 release.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>kubernetes</category>
      <category>cloud</category>
      <category>rust</category>
    </item>
    <item>
      <title>What's next for AssemblyLift? Data-oriented cloud dev through WebAssembly and capability-based security</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Tue, 19 Oct 2021 16:07:45 +0000</pubDate>
      <link>https://forem.com/akkoro/whats-next-for-assemblylift-data-oriented-cloud-dev-through-webassembly-and-capability-based-security-fn3</link>
      <guid>https://forem.com/akkoro/whats-next-for-assemblylift-data-oriented-cloud-dev-through-webassembly-and-capability-based-security-fn3</guid>
      <description>&lt;p&gt;After nearly two years in part-time development, &lt;a href="https://assemblylift.akkoro.io"&gt;AssemblyLift&lt;/a&gt; has been through three major revisions, sitting now at v0.3.2.&lt;/p&gt;

&lt;p&gt;The focus up to now has been on creating a low-friction environment for building applications with functions and APIs, and on the core functionality of the WebAssembly runtime. Using a simple TOML definition, AssemblyLift makes it easy to deploy Rust functions to AWS Lambda fronted by API Gateway. TL;DR it's a nifty little API-oriented compute framework!&lt;/p&gt;

&lt;p&gt;Conspicuously missing from this framework however is any notion of &lt;em&gt;data&lt;/em&gt;. Where do we store it? Who is allowed to access it? I've personally never written a dynamic web service that didn't need to interact with a data store in some way or another!&lt;/p&gt;

&lt;p&gt;It's common to pair serverless compute like Lambda with a serverless database like DynamoDB. This pairing requires the Lambda function to have an attached IAM policy granting it the necessary access to DynamoDB resources. With AssemblyLift currently, attaching IAM policies is done either via the AWS Console or by including your own Terraform code with the AssemblyLift project (this process is covered in &lt;a href="https://dev.to/akkoro/lambda-function-http-authorization-with-auth0-and-assemblylift-webassembly-lambda-api-gateway-rust-4fl8"&gt;another blog post&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This service-to-service permissions model is common among cloud providers, and is usually implemented as Role-Based Access Control or &lt;em&gt;RBAC&lt;/em&gt;. In our example above, a Lambda function is assigned an &lt;em&gt;execution role&lt;/em&gt; which has one or more policies attached. If the function is allowed to assume the role, and if the attached policies allow it the function can access DynamoDB. Or more accurately, it can access whichever DDB resources are specified in the policy &lt;code&gt;resources&lt;/code&gt; list. AWS policies also allow you to specify a list of &lt;em&gt;conditions&lt;/em&gt; on the data which can be accessed, for example by only allowing &lt;code&gt;GetItem&lt;/code&gt; for items with a particular prefix on the primary key. &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/access-control-overview.html"&gt;The official AWS docs&lt;/a&gt; contain more details on DynamoDB access management.&lt;/p&gt;

&lt;p&gt;In a serverless context, this model of access control can create some problems. To maintain the &lt;a href="https://www.upguard.com/blog/principle-of-least-privilege"&gt;Principle of Least Privilege&lt;/a&gt; it is recommended to keep a one-to-one relationship between functions and roles, i.e. functions should not share roles. At scale you may have hundreds of functions, for each of which a unique access policy is maintained. This means that changes to access policy for any data potentially has to be replicated many times. This complexity increases the risk of having misconfigured and/or inconsistent access policies, even if they are represented as code. Using policy conditions to restrict data access is problematic, as your database "schema" (the format of your keys) is then hard-coded separately from the function code.&lt;/p&gt;

&lt;p&gt;We didn't want to expose this complexity in AssemblyLift. It's the kind of "plumbing" code nobody enjoys writing, and it's also the kind that tends to get glossed over simply because it's hard to get right. Fortunately, it is now 2021 and we have some new tools at our disposal!&lt;/p&gt;

&lt;h2&gt;
  
  
  Entity-Capability Data Access Control
&lt;/h2&gt;

&lt;p&gt;Entity-Capability Data Access Control (ECDAC) is the tentative name for the data access model being developed for AssemblyLift (as a kind of nod to the &lt;a href="https://en.wikipedia.org/wiki/Entity_component_system"&gt;Entity-Component-System&lt;/a&gt; architecture which inspired it). The model is intended to alleviate the issues with policy definition described above. It is also meant to address issues with maintaining &amp;amp; communicating entity/object schema definitions between many functions and services, which will be discussed below.&lt;/p&gt;

&lt;p&gt;In this scheme there are (as you might have guessed) two essential components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entities&lt;/li&gt;
&lt;li&gt;Capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we talk about an &lt;em&gt;Entity&lt;/em&gt;, we mean an object of some kind with a unique ID (e.g. Person, Account, Post, Comment, etc). This is the same sort of entity concept which is often used in database design, including &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-modeling-nosql-B.html"&gt;DynamoDB "single-table" design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Capabilities&lt;/em&gt; refers to capabilities in the capability-based computing/security sense. Connor Hicks at Suborbital &lt;a href="https://blog.suborbital.dev/reorienting-around-capability-based-computing"&gt;wrote an excellent blog post&lt;/a&gt; which discusses the details and benefits of the capability-based model better than I probably can here. In short, capabilities define what an application component &lt;em&gt;can do&lt;/em&gt;. A Capability is typically some kind of unforgeable token or object, which embeds a set of access rights.&lt;/p&gt;

&lt;p&gt;An important distinction between access rights encoded as a Capability, and access rights encoded in an RBAC policy, is that &lt;em&gt;&lt;strong&gt;Capabilities can be transmitted and shared&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;&lt;strong&gt;possessing a valid Capability implies valid access&lt;/strong&gt;&lt;/em&gt;. By possessing a valid Capability, application code can verify that it is allowed to perform an action. In an RBAC system on the other hand the entire function must assume a role (effectively pretending to be a "user"), and in turn take on the union of all permissions provided by the attached policies. Access is validated independently of the function code (after the code has already executed) by an authorization service and rejected if the policy doesn't allow the action.&lt;/p&gt;

&lt;p&gt;In our ECDAC model, an entity is composed of a unique ID, a &lt;em&gt;shape&lt;/em&gt; describing any necessary schema, and a set of &lt;em&gt;capabilities&lt;/em&gt; defining how data described by this entity can be accessed. This inversion of control -- keeping access control with the data rather than the service -- is made possible by using WebAssembly modules to implement our Entities!&lt;/p&gt;

&lt;p&gt;The implementation details are still being worked out, but the central idea is that our data Capability is compiled as an executable WASM module, which is an Entity insofar as it implements the Entity ABI. An Entity is a Capability as a WASM module.&lt;/p&gt;

&lt;p&gt;A service defines an Entity as a kind of dependency. This means the service possesses an Entity, which in turn means possessing a Capability. An AssemblyLift guest will interact with Entities through a high-level API, which delegates to the Entity module to verify the Capability, and operate on data in a particular location according to the Entity shape/schema. The Entity module is self-verifying and needs no outside processes to validate the requested action. &lt;em&gt;If&lt;/em&gt; we want Entities to &lt;em&gt;perform&lt;/em&gt; database operations on behalf of the guest, they will need either an &lt;a href="https://docs.assemblylift.akkoro.io/learn-assemblylift/io-modules"&gt;IOmod&lt;/a&gt; or WASI environment. Otherwise it will return back to the AssemblyLift host to complete the action (I'm personally torn on what the best choice here is 🙃).&lt;/p&gt;

&lt;p&gt;An example Entity definition for an AssemblyLift project might look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[entity]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;

&lt;span class="nn"&gt;[[fields]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;
&lt;span class="py"&gt;attributes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["searchable"]&lt;/span&gt; &lt;span class="c"&gt;# Optional&lt;/span&gt;

&lt;span class="nn"&gt;[[fields]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;
&lt;span class="py"&gt;attributes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"unique"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Optional&lt;/span&gt;

&lt;span class="nn"&gt;[[actions]]&lt;/span&gt;
&lt;span class="py"&gt;action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"load"&lt;/span&gt;
&lt;span class="nn"&gt;[actions.cap]&lt;/span&gt;
&lt;span class="py"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"asml/service/service-name/*"&lt;/span&gt;
&lt;span class="py"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"allow"&lt;/span&gt;

&lt;span class="nn"&gt;[[actions]]&lt;/span&gt;
&lt;span class="py"&gt;action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"store"&lt;/span&gt;
&lt;span class="nn"&gt;[actions.cap]&lt;/span&gt;
&lt;span class="py"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dynamodb/region/table-name"&lt;/span&gt;
&lt;span class="py"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"allow"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have an Entity representing a &lt;em&gt;user&lt;/em&gt; which has defined a &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; field. The entity specifies two actions &lt;code&gt;load&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt;, each of which have an associated Capability. The &lt;code&gt;load&lt;/code&gt; action specifies a &lt;code&gt;location&lt;/code&gt;, which is a unique identifier specifying a resource which can perform the action on the Entity (i.e. the Entity says "who can retrieve me?"). Similarly the &lt;code&gt;store&lt;/code&gt; action specifies a &lt;code&gt;location&lt;/code&gt; indicating where it can be stored (i.e. the Entity says "who can receive me?").&lt;/p&gt;

&lt;p&gt;From within our function code, we can interact with an API which abstracts the low-level Entity system implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;user_prototype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;user_prototype&lt;/span&gt;&lt;span class="nf"&gt;.query_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see a number of benefits over traditional RBAC/IAM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced policy complexity&lt;/strong&gt;: by defining access in terms of database entities, we eliminate the need to maintain unique policies for each function in an application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified access definition&lt;/strong&gt;: data-oriented access control decouples resource access from data access, and lets us reduce the surface area of the policy language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved data access transparency&lt;/strong&gt;: "who can access this data?" is answered by the data's Entity definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent entity schema&lt;/strong&gt;: the shape of an Entity is stored in the module, distributing schema rules to every function which has the Entity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When is this coming?
&lt;/h2&gt;

&lt;p&gt;Development is in progress now, and the plan as of writing is to get a version of this introduced in v0.4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.sciencedirect.com/science/article/pii/S089571771300054X"&gt;Gusmeroli et al. published a paper in 2013&lt;/a&gt; describing many of these limitations of RBAC in the context of the Internet of Things (IoT). Role management for a large fleet of devices and for a large fleet of services &amp;amp; functions has many parallels.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>rust</category>
      <category>serverless</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Lambda function HTTP authorization with Auth0 and AssemblyLift (WebAssembly + Lambda + API Gateway + Rust)</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Sat, 09 Oct 2021 13:34:06 +0000</pubDate>
      <link>https://forem.com/akkoro/lambda-function-http-authorization-with-auth0-and-assemblylift-webassembly-lambda-api-gateway-rust-4fl8</link>
      <guid>https://forem.com/akkoro/lambda-function-http-authorization-with-auth0-and-assemblylift-webassembly-lambda-api-gateway-rust-4fl8</guid>
      <description>&lt;p&gt;Oh, hello! Sorry, I didn't see you there.&lt;/p&gt;

&lt;p&gt;Since you're here, let's talk about &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; function authorization!&lt;/p&gt;

&lt;p&gt;By itself, the only way a Lambda function has to reject an invocation is with an IAM policy. This will let you prevent another service from &lt;em&gt;programmatically&lt;/em&gt; invoking the function via the AWS SDK. In most cases however it's considered bad practice to directly invoke a function in this way, due to the coupling it can introduce between two services.&lt;/p&gt;

&lt;p&gt;A better approach is to invoke the function via HTTP &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt;, and make the underlying function an implementation detail. In addition to decoupling, with an API Gateway we can now use HTTP authorization schemes such as OAuth to protect our functions!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; offers a highly-regarded (and easy to use, personally speaking) authentication platform, which we can use to provide an OAuth authorizer for our API. Auth0 offers a free plan supporting up to 7000 users!&lt;/p&gt;

&lt;p&gt;In this guide we'll use &lt;a href="https://assemblylift.akkoro.io" rel="noopener noreferrer"&gt;AssemblyLift&lt;/a&gt; to deploy our Lambda function, and define our API and an authorizer. AssemblyLift is an open platform which allows you to quickly develop high-performance serverless applications. Service functions are written in the Rust programming language and compiled to WebAssembly. The AssemblyLift CLI takes care of compiling and deploying your code, as well as building and deploying all the needed infrastructure!&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparation &amp;amp; assumed knowledge
&lt;/h2&gt;

&lt;p&gt;To follow this guide you will need an &lt;a href="https://portal.aws.amazon.com/billing/signup#/start" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; if you do not have one already.&lt;/p&gt;

&lt;p&gt;You will also need the &lt;a href="https://rustup.rs/" rel="noopener noreferrer"&gt;Rust toolchain&lt;/a&gt; and &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;NPM&lt;/a&gt; installed. The Rust toolchain is installed using the &lt;code&gt;rustup&lt;/code&gt; interactive installer. The default options during installation should be fine. After installation you will need to install the &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; build target for the Rust toolchain (&lt;code&gt;rustup toolchain install wasm32-unknown-unknown&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Once you have these prerequisites you can install AssemblyLift with &lt;code&gt;cargo install assemblylift-cli&lt;/code&gt;. Run &lt;code&gt;asml help&lt;/code&gt; to verify the installation.&lt;/p&gt;

&lt;p&gt;⚠️&lt;strong&gt;NOTE&lt;/strong&gt;⚠️: if you have previously installed AssemblyLift, please make sure you are on the latest version before you begin! Version &lt;code&gt;0.3.2&lt;/code&gt; contains bugfixes affecting this guide. 🙂&lt;/p&gt;

&lt;h3&gt;
  
  
  Assumed knowledge
&lt;/h3&gt;

&lt;p&gt;We are going to write a simple function in &lt;a href="https://rust-lang.org" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; which writes an item to a &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;DynamoDB&lt;/a&gt; table, so you should be familiar with Rust (our usage of DynamoDB will be very basic). In particular, you should be comfortable with &lt;a href="https://dev.to/dotxlem/async-rust-but-less-intimidating-2c13"&gt;async/await in Rust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While this guide should be fairly straightforward, we will be using some &lt;em&gt;slightly&lt;/em&gt; more advanced features of AssemblyLift. If this is your first time using AssemblyLift, consider taking a look &lt;a href="https://dev.to/akkoro/deploy-an-ultra-fast-blog-in-minutes-with-eleventy-and-assemblylift-webassembly-lambda-api-gateway-rust-568l"&gt;this post on deploying an Eleventy static site with AssemblyLift&lt;/a&gt; which is a little simpler.&lt;/p&gt;

&lt;p&gt;You should also be familiar with AWS IAM, as we will need to add permissions to our function enabling access to DynamoDB. Optionally if you know some &lt;a href="https://terraform.io" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, we will also demonstrate how you can set these permissions from within the project code. 😀&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;In your favourite &lt;em&gt;projects&lt;/em&gt; directory, create a new AssemblyLift application and change to that directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asml init -n auth-tutorial
$ cd auth-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's start by adding a &lt;code&gt;data&lt;/code&gt; service, in which we will have a &lt;code&gt;put&lt;/code&gt; function that allows us to add a new item to a DynamoDB table.&lt;/p&gt;

&lt;p&gt;Generate the new service &amp;amp; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asml make service data
$ asml make function data.put
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order for AssemblyLift to recognize the new service, it must be added to the project manifest &lt;code&gt;assemblylift.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"auth0-tutorial"&lt;/span&gt;

&lt;span class="nn"&gt;[services]&lt;/span&gt;
&lt;span class="py"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you like, you can delete the &lt;code&gt;default&lt;/code&gt; service from the manifest as well as remove its directory under &lt;code&gt;services/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an Auth0 API
&lt;/h3&gt;

&lt;p&gt;With the base of our AssemblyLift project set up, now would be a good time to create a new API in Auth0. This will provide JWT authorization to our &lt;code&gt;data.put&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;In your Auth0 dashboard, navigate to &lt;em&gt;Applications &amp;gt; APIs&lt;/em&gt; and hit &lt;em&gt;Create API&lt;/em&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltiqrtqjaihwiavrhswo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltiqrtqjaihwiavrhswo.png" alt="A screenshot of the "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave the signing algorithm as RS256, and give your API a name. The &lt;em&gt;identifier&lt;/em&gt; is a unique string which is used within OAuth 2.0 to identify the authorization server which issued a token. It is recommended to use the URL of the endpoint which will be associated with this authorizer, however you are free to use whichever naming scheme you like!&lt;/p&gt;
&lt;h2&gt;
  
  
  Configure the &lt;code&gt;data&lt;/code&gt; service
&lt;/h2&gt;

&lt;p&gt;Next let's open up the data service manifest &lt;code&gt;services/data/service.toml&lt;/code&gt;, and replace the default configuration with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# data/service.toml&lt;/span&gt;
&lt;span class="nn"&gt;[service]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt;

&lt;span class="nn"&gt;[api.functions.put]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"put"&lt;/span&gt;
&lt;span class="py"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;verb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PUT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/put"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;authorizer_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"auth0"&lt;/span&gt;

&lt;span class="nn"&gt;[api.authorizers.auth0]&lt;/span&gt;
&lt;span class="py"&gt;auth_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"JWT"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure the authorizer
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;JWT&lt;/code&gt; authorizer at minimum requires &lt;code&gt;audience&lt;/code&gt; and &lt;code&gt;issuer&lt;/code&gt; parameters. The &lt;em&gt;audience&lt;/em&gt; is the &lt;em&gt;identifer&lt;/em&gt; you chose for your API in the Auth0 console. The &lt;em&gt;issuer&lt;/em&gt; is going to be a URL of the form &lt;code&gt;https://&amp;lt;tenant-id&amp;gt;.&amp;lt;region&amp;gt;.auth0.com&lt;/code&gt;. You can find your tenant ID and region on the &lt;em&gt;settings&lt;/em&gt; tab of the Auth0 console.&lt;/p&gt;

&lt;p&gt;Add the parameters to the &lt;code&gt;auth0&lt;/code&gt; definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# data/service.toml&lt;/span&gt;
&lt;span class="nn"&gt;[api.authorizers.auth0]&lt;/span&gt;
&lt;span class="py"&gt;auth_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"JWT"&lt;/span&gt;
&lt;span class="py"&gt;audience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"your-identifier-here"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;issuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://&amp;lt;tenant-id&amp;gt;.&amp;lt;region&amp;gt;.auth0.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Writing data with the &lt;code&gt;put&lt;/code&gt; function
&lt;/h2&gt;

&lt;p&gt;This will be a relatively simple function which writes some data to a DynamoDB table, as an example of an operation that really ought to be protected by an authorizer 😜&lt;/p&gt;

&lt;p&gt;We will need to add a dependency to the DynamoDB &lt;a href="https://docs.assemblylift.akkoro.io/learn-assemblylift/io-modules/registry" rel="noopener noreferrer"&gt;IOmod&lt;/a&gt;. IOmod dependencies are defined in &lt;code&gt;service.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# data/service.toml&lt;/span&gt;
&lt;span class="nn"&gt;[iomod.dependencies.dynamodb]&lt;/span&gt;
&lt;span class="py"&gt;coordinates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"akkoro.aws.dynamodb"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also need to add a Cargo dependency to our &lt;code&gt;put&lt;/code&gt; function. IOmod dependencies are paired with "guest crates" which provide an API to the IOmod. If you are familiar with the C or C++ programming language, you can think of this as analogous to a header file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Cargo.toml&lt;/span&gt;
&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;assemblylift-iomod-dynamodb-guest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next let's look at the function code. Find &lt;code&gt;put/src/lib.rs&lt;/code&gt; and update it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// data/put/src/lib.rs&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;asml_awslambda&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GuestCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_awslambda&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;assemblylift_iomod_dynamodb_guest&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;structs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;handler!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LambdaContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiGatewayEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.event.request_context&lt;/span&gt;
                &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="py"&gt;.authorizer&lt;/span&gt;
                &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="py"&gt;.claims&lt;/span&gt;
                &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PutItemInput&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"auth0-tutorial"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Change this to the name of your own DynamoDB table!&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PutItemInputAttributeMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;pk_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AttributeValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;pk_value&lt;/span&gt;&lt;span class="py"&gt;.s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="py"&gt;.item&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;AttributeName&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pk"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;pk_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;http_ok!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;http_error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function calls the DynamoDB &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html" rel="noopener noreferrer"&gt;&lt;code&gt;PutItem&lt;/code&gt;&lt;/a&gt; action, to write the authenticated user's ID to a table called &lt;code&gt;auth0-tutorial&lt;/code&gt; with a primary key named &lt;code&gt;pk&lt;/code&gt;. The user ID is written as the primary key, as you might if this were a real table tracking user data.&lt;/p&gt;

&lt;p&gt;All IOmod calls in Rust return a type implementing &lt;code&gt;Future&lt;/code&gt;, giving us support for &lt;code&gt;await&lt;/code&gt; syntax! The action is called with the &lt;code&gt;input&lt;/code&gt; struct we built, and an HTTP response is returned when the action is resolved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Function permissions
&lt;/h3&gt;

&lt;p&gt;In order for our function to access DynamoDB, we will have to attach an IAM policy to the function's execution role which allows access.&lt;/p&gt;

&lt;p&gt;Unfortunately this is not handled in the AssemblyLift TOML directly at the moment (we are taking our time sorting out how permissions will be modelled in AssemblyLift 🙂). Your options are to find the role in the &lt;a href="https://console.aws.amazon.com/iamv2/home?#/roles" rel="noopener noreferrer"&gt;AWS IAM console&lt;/a&gt; and add the policy there, or you can use a feature of AssemblyLift which lets you include your own Terraform code with the code generated by the CLI!&lt;/p&gt;

&lt;p&gt;AssemblyLift generates IAM roles for functions named in a particular format; &lt;code&gt;asml-{project name}-{service name}-{function name}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your own Terraform can be included with the AssemblyLift project by adding a Terraform module inside a directory named &lt;code&gt;user_tf&lt;/code&gt; at the project root (next to &lt;code&gt;assemblylift.toml&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;An example of such a module is below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;aws_caller_identity&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;aws_region&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_caller_identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_id&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;

    &lt;span class="nx"&gt;prefix&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"asml-auth0-tutorial"&lt;/span&gt;
    &lt;span class="nx"&gt;table&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"auth0-tutorial"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt; &lt;span class="nx"&gt;data-put&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-data-put"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt; &lt;span class="nx"&gt;allow-dynamodb-put&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dynamodb:PutItem"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;effect&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:dynamodb:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account_id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:table/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_policy&lt;/span&gt; &lt;span class="nx"&gt;data-put-policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data-put&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-dynamodb"&lt;/span&gt;
    &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow-dynamodb-put&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role_policy_attachment&lt;/span&gt; &lt;span class="nx"&gt;data-put-policy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data-put&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data-put-policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform's &lt;code&gt;data&lt;/code&gt; lookup feature is used to import the role AssemblyLift created for you. A caveat here is that you must have run &lt;code&gt;asml bind&lt;/code&gt; at least once prior to adding this lookup, in order for it to be found! See the next section below on building &amp;amp; deploying!&lt;/p&gt;

&lt;h2&gt;
  
  
  Build &amp;amp; deploy the &lt;code&gt;data&lt;/code&gt; service!
&lt;/h2&gt;

&lt;p&gt;This part is quite easy with AssemblyLift! ☺️&lt;/p&gt;

&lt;p&gt;Use the &lt;code&gt;cast&lt;/code&gt; command to compile your function code, and generate a Terraform plan. Then use the &lt;code&gt;bind&lt;/code&gt; command to deploy the serialized code &amp;amp; infra to AWS!&lt;/p&gt;

&lt;p&gt;Before running &lt;code&gt;bind&lt;/code&gt;, check the output of all the processes executed during &lt;code&gt;cast&lt;/code&gt; to verify that the function compiled OK and that the infrastructure changes reported by Terraform aren't unexpected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;asml cast
&lt;span class="nv"&gt;$ &lt;/span&gt;asml &lt;span class="nb"&gt;bind&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing our function authorizer
&lt;/h2&gt;

&lt;p&gt;The easiest way to test the authorizer is to use the &lt;code&gt;curl&lt;/code&gt; commands generated for you in the Auth0 dashboard! Navigate to the &lt;em&gt;test&lt;/em&gt; tab of your API in the dashboard. The command you are looking for is of the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://path_to_your_api/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer &amp;lt;TOKEN&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your API endpoint URL can be found in the &lt;a href="https://console.aws.amazon.com/apigateway/main/apis" rel="noopener noreferrer"&gt;AWS API Gateway console&lt;/a&gt;. This endpoint corresponds to the &lt;code&gt;data&lt;/code&gt; service. Our &lt;code&gt;put&lt;/code&gt; function is at the path (and verb) we defined in the service definition TOML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; PUT &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://&amp;lt;api-id&amp;gt;.execute-api.&amp;lt;region&amp;gt;.amazonaws.com/put &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'authorization: Bearer &amp;lt;TOKEN&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling the endpoint &lt;strong&gt;without&lt;/strong&gt; the authorization header should return an HTTP Forbidden error. Otherwise it should return HTTP OK with an empty JSON object &lt;code&gt;{}&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Open the &lt;a href="https://console.aws.amazon.com/dynamodbv2/home" rel="noopener noreferrer"&gt;DynamoDB web console&lt;/a&gt; and verify that the PutItem call worked; there should be an item with your Auth0 user ID!&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all, folks!
&lt;/h2&gt;

&lt;p&gt;If you have any questions, please don't hesitate to reach out in the comments below or &lt;a href="https://github.com/akkoro/assemblylift/discussions" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>rust</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Deploy an ultra-fast blog in minutes with Eleventy and AssemblyLift (WebAssembly + Lambda + API Gateway + Rust)</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Tue, 28 Sep 2021 20:49:55 +0000</pubDate>
      <link>https://forem.com/akkoro/deploy-an-ultra-fast-blog-in-minutes-with-eleventy-and-assemblylift-webassembly-lambda-api-gateway-rust-568l</link>
      <guid>https://forem.com/akkoro/deploy-an-ultra-fast-blog-in-minutes-with-eleventy-and-assemblylift-webassembly-lambda-api-gateway-rust-568l</guid>
      <description>&lt;p&gt;Hello fellow developers! 😊&lt;/p&gt;

&lt;p&gt;Let's talk about static site generators for a minute. Static Site Generators (SSGs) are popular these days for lots of applications, not least of all personal blogs. Static sites offer quick response times owing to easy caching by CDNs. They also have the advantage of not needing access to a database for content. Perhaps best of all, they can often be hosted for free using a simple S3 bucket or a SaaS such as &lt;a href="https://netlify.com"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An alternative approach is a serverless backend with AWS Lambda and API Gateway. This would allow our "static" file server to do some processing before serving the final HTML if we like. With Lambda, we can control our "server" performance by adjusting the function's memory/CPU allocation. Using API Gateway allows us to monitor and secure our routes (among other things). Lots of benefits while still being cheap, if not free, and high-performance!&lt;/p&gt;

&lt;p&gt;Normally what we've described above would be a pretty complicated set up (and a much longer article 😜). Luckily we can use &lt;a href="https://assemblylift.akkoro.io"&gt;AssemblyLift&lt;/a&gt; to do all the heavy work for us!&lt;/p&gt;

&lt;p&gt;AssemblyLift is an open platform which allows you to quickly develop high-performance serverless applications. Service functions are written in the Rust programming language and compiled to WebAssembly. The AssemblyLift CLI takes care of compiling and deploying your code, as well as building and deploying all the needed infrastructure!&lt;/p&gt;

&lt;p&gt;In this walkthrough we're going to build a blog using &lt;a href="https://11ty.dev"&gt;Eleventy&lt;/a&gt; (11ty), a JavaScript SSG. While I was preparing to write this I started putting together my own blog; the source of which you can &lt;a href="https://github.com/dotxlem/xlem-blog"&gt;take a look at on GitHub&lt;/a&gt; if you'd like to see approximately what we'll be building (your's will undoubtedly be prettier than mine).&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparation &amp;amp; assumed knowledge
&lt;/h2&gt;

&lt;p&gt;To follow this guide you will need an &lt;a href="https://portal.aws.amazon.com/billing/signup#/start"&gt;AWS account&lt;/a&gt; if you do not have one already.&lt;/p&gt;

&lt;p&gt;You will also need the &lt;a href="https://rustup.rs/"&gt;Rust toolchain&lt;/a&gt; and &lt;a href="https://nodejs.org"&gt;NPM&lt;/a&gt; installed. The Rust toolchain is installed using the &lt;code&gt;rustup&lt;/code&gt; interactive installer. The default options during installation should be fine. After installation you will need to install the &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; build target for the Rust toolchain (&lt;code&gt;rustup toolchain install wasm32-unknown-unknown&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Once you have these prerequisites you can install AssemblyLift with &lt;code&gt;cargo install assemblylift-cli&lt;/code&gt;. Run &lt;code&gt;asml help&lt;/code&gt; to verify the installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assumed knowledge
&lt;/h3&gt;

&lt;p&gt;AssemblyLift functions are written in Rust, so it will be helpful to have a working knowledge of Rust. That said if you are only interested in getting your site deployed, you can safely smile and nod at the Rust code and skip ahead 🙃. Eleventy is a JavaScript framework, however the only JS you &lt;em&gt;need&lt;/em&gt; is its configuration in &lt;code&gt;.eleventy.js&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;In your favourite &lt;em&gt;projects&lt;/em&gt; directory, create a new AssemblyLift application for your blog and change to that directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asml init -n my-blog
$ cd my-blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we can configure our Eleventy front-end, we'll need to set up our AssemblyLift backend and create our web service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the default project
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;asml init&lt;/code&gt; a new AssemblyLift project is generated with the bare minimum needed to build and deploy an application; a single service containing a single function.&lt;/p&gt;

&lt;p&gt;First let's take a look at the &lt;em&gt;application manifest&lt;/em&gt;, &lt;code&gt;assemblylift.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generated with assemblylift-cli&lt;/span&gt;

&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-blog"&lt;/span&gt;

&lt;span class="nn"&gt;[services]&lt;/span&gt;
&lt;span class="nn"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-service"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need a service which will serve our site assets, so we can start by renaming the default service. Update the &lt;em&gt;services&lt;/em&gt; table and name the service something relevant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# assemblylift.toml&lt;/span&gt;
&lt;span class="nn"&gt;[services]&lt;/span&gt;
&lt;span class="nn"&gt;web&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also need to rename the service's directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;services/my-service services/web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next let's open up the &lt;code&gt;web&lt;/code&gt; service manifest, &lt;code&gt;services/web/service.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generated with assemblylift-cli&lt;/span&gt;

&lt;span class="nn"&gt;[service]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-service"&lt;/span&gt;

&lt;span class="nn"&gt;[api.functions.my-function]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-function"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rename the service to &lt;code&gt;web&lt;/code&gt;. Then as with our service in the project manifest, we should rename the default function in the service manifest to something relevant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# web/service.toml&lt;/span&gt;
&lt;span class="nn"&gt;[service]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"web"&lt;/span&gt;

&lt;span class="nn"&gt;[api.functions.server]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And rename the function directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;services/web/my-function services/web/server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we'll need to rename the function inside &lt;code&gt;web/server/Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# server/Cargo.toml&lt;/span&gt;
&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0"&lt;/span&gt;
&lt;span class="err"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The server function
&lt;/h2&gt;

&lt;p&gt;Let's take another look inside the service manifest, at the &lt;em&gt;api.functions&lt;/em&gt; table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# web/service.toml&lt;/span&gt;
&lt;span class="nn"&gt;[api.functions.server]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty sparse right? As is, this will deploy a lambda function without any sort of API (though it can still be invoked like any other function via the AWS SDK!). Let's add an HTTP route to our function so we can invoke it from our browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# web/service.toml&lt;/span&gt;
&lt;span class="nn"&gt;[api.functions.server]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"server"&lt;/span&gt;
&lt;span class="nn"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;verb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/{path+}"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;http&lt;/code&gt; block has a &lt;code&gt;verb&lt;/code&gt; and a &lt;code&gt;path&lt;/code&gt;. The verb is the HTTP method the function is invoked with (e.g. GET, POST, PUT, etc.). The path (or "route" in some frameworks) is the HTTP path which is mapped to our function. In this case we are using a feature of API Gateway called a &lt;em&gt;proxy path&lt;/em&gt;. This is the &lt;code&gt;{path+}&lt;/code&gt; variable, which globs the entire path as a single variable called &lt;code&gt;path&lt;/code&gt; inside our function code.&lt;/p&gt;

&lt;h3&gt;
  
  
  The function code
&lt;/h3&gt;

&lt;p&gt;So far we've got our infrastructure defined, but the default function code doesn't do much of anything.&lt;/p&gt;

&lt;p&gt;Open up &lt;code&gt;web/server/src/lib.rs&lt;/code&gt;, and overwrite it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// lib.rs&lt;/span&gt;
&lt;span class="c"&gt;// www/server&lt;/span&gt;

&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="n"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;asml_awslambda&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;flate2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;write&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GzEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;flate2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Compression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;mime_guess&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rust_embed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RustEmbed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_awslambda&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GuestCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;handler!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LambdaContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiGatewayEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.event.path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.ends_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
        &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nn"&gt;AwsLambdaClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;console_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Serving {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;PublicAssets&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;GzEncoder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;Compression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nn"&gt;mime_guess&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                    &lt;span class="nf"&gt;.first_or_octet_stream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="nf"&gt;.finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="nd"&gt;http_ok!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// true, true: we always gzip &amp;amp; encode base64&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;http_not_found!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(RustEmbed)]&lt;/span&gt;
&lt;span class="nd"&gt;#[folder&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"../../../www/_site"&lt;/span&gt;&lt;span class="nd"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PublicAssets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function uses the &lt;a href="https://crates.io/crates/rust-embed"&gt;&lt;code&gt;rust-embed&lt;/code&gt;&lt;/a&gt; crate, which embeds the contents of a directory inside the compiled binary. We use this to store the site generated by Eleventy inside the function!&lt;/p&gt;

&lt;p&gt;We can then use the &lt;code&gt;path&lt;/code&gt; variable passed to the function via the function input (remember our path parameter, &lt;code&gt;{path+}&lt;/code&gt;?) to select which resource to return as our function output. A &lt;code&gt;match&lt;/code&gt; block is used to detect if we have a path ending in a forward slash, indicating that we should default to serving &lt;code&gt;index.html&lt;/code&gt; from that path.&lt;/p&gt;

&lt;p&gt;Other crates are imported as well, to handle Gzip &amp;amp; Base 64 encoding and as well as guessing MIME types for our response headers. In all, you should have the following dependencies in your &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;base64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.13"&lt;/span&gt;
&lt;span class="py"&gt;direct-executor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3.0"&lt;/span&gt;
&lt;span class="py"&gt;flate2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;
&lt;span class="py"&gt;mime_guess&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;
&lt;span class="py"&gt;rust-embed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6"&lt;/span&gt;
&lt;span class="py"&gt;serde&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;
&lt;span class="py"&gt;serde_json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;
&lt;span class="nn"&gt;asml_core&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;package&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"assemblylift-core-guest"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nn"&gt;assemblylift_core_io_guest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;package&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"assemblylift-core-io-guest"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nn"&gt;asml_awslambda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;package&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"assemblylift-awslambda-guest"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Eleventy
&lt;/h2&gt;

&lt;p&gt;As of now our base infrastructure is ready, but compiling our function will fail because our &lt;code&gt;www&lt;/code&gt; directory doesn't exist yet! Let's fix that; first by creating the directory in our project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;www
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new package in this directory with &lt;code&gt;npm&lt;/code&gt; and then install Eleventy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @11ty/eleventy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, it will be helpful to take a look at the &lt;a href="https://github.com/11ty/eleventy-base-blog"&gt;Eleventy base blog repo&lt;/a&gt; on GitHub. I recommend copying &lt;code&gt;.eleventy.js&lt;/code&gt; into &lt;code&gt;www&lt;/code&gt; and installing these additional packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev @11ty/eleventy-navigation @11ty/eleventy-plugin-rss @11ty/eleventy-plugin-syntaxhighlight luxon markdown-it markdown-it-anchor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there the structure of your site is really up to you! Look at examples and determine what you like best. &lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;npx @11ty/eleventy&lt;/code&gt; will generate the static site from any template files it finds in the current directory, and by default write the output to &lt;code&gt;_site/&lt;/code&gt;. You will at least need &lt;code&gt;index.*&lt;/code&gt; in order to generate an &lt;code&gt;index.html&lt;/code&gt; to serve.&lt;/p&gt;

&lt;p&gt;A powerful feature of Eleventy is &lt;a href="https://www.11ty.dev/docs/collections/"&gt;collections&lt;/a&gt;. Pages with the same tag are grouped into a collection by the same name, and made available inside our templates. In the base blog repo for example, all posts are placed in a directory and a single JSON file specifies the tags assigned to everything in the directory. Now adding a post to your blog is as easy as adding a new file to your &lt;code&gt;posts&lt;/code&gt; directory!&lt;/p&gt;

&lt;h2&gt;
  
  
  Build &amp;amp; deploy our new site
&lt;/h2&gt;

&lt;p&gt;Once you have something which builds without error in &lt;code&gt;www/_site&lt;/code&gt;, you should have everything necessary to build our application and deploy!&lt;/p&gt;

&lt;p&gt;AssemblyLift applications are built with the &lt;code&gt;cast&lt;/code&gt; command, inside the project root (the directory where &lt;code&gt;assemblylift.toml&lt;/code&gt; is found):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;asml cast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will compile our web service &amp;amp; server function, and generate a Terraform infrastructure plan. All build artifacts are serialized in the &lt;code&gt;net/&lt;/code&gt; directory. When our Rust function is compiled, our static site assets are bundled with the resulting WebAssembly binary!&lt;/p&gt;

&lt;p&gt;To deploy our new service, simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;asml &lt;span class="nb"&gt;bind&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the web service
&lt;/h3&gt;

&lt;p&gt;If everything in the &lt;code&gt;bind&lt;/code&gt; command completed without error, you should now be able to find a new API Gateway endpoint inside the AWS console. The API will be named &lt;code&gt;asml-{projectName}-{serviceName}&lt;/code&gt;. Navigate to your API details, where you should find an endpoint listed for the &lt;code&gt;$default&lt;/code&gt; stage. Opening this URL in a browser should render your new statically generated blog!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further enhancements
&lt;/h2&gt;

&lt;p&gt;For production use, you will probably want to create a CloudFront distribution using your web server service as the distribution source. The API Gateway service also supports custom domain names, as the generated APIGW/CloudFront URLs are quite ugly 😁.&lt;/p&gt;

&lt;p&gt;If in the future you want to expand your blog with dynamic content, you can create additional services &amp;amp; functions. Note however that if you intend on using CloudFront or other CDN, the &lt;code&gt;web&lt;/code&gt; service should &lt;strong&gt;only&lt;/strong&gt; contain the &lt;code&gt;server&lt;/code&gt; function and all other functionality should reside in other services. This is because the server function uses a proxy path; no other functions in the service would receive invocations!&lt;/p&gt;

&lt;p&gt;In AssemblyLift a new service is created by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asml make service my-service-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then add a new function to the service with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ asml make function my-service-name.my-function-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any new functions you make which you don't want to be publicly accessible should attach an authorizer. Take a look at &lt;a href="https://dev.to/akkoro/lambda-function-http-authorization-with-auth0-and-assemblylift-webassembly-lambda-api-gateway-rust-4fl8"&gt;this post on using Auth0 with AssemblyLift&lt;/a&gt; for an in-depth guide!&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all, folks!
&lt;/h2&gt;

&lt;p&gt;If you have any questions, don't hesitate to reach out in the comments below or &lt;a href="https://github.com/akkoro/assemblylift/discussions"&gt;on GitHub&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;For more details on AssemblyLift, please see the &lt;a href="https://docs.assemblylift.akkoro.io"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>rust</category>
      <category>webdev</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Deploy a Jamstack site on AWS Lambda with API Gateway in 10 minutes or less 💨</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Thu, 12 Aug 2021 16:48:53 +0000</pubDate>
      <link>https://forem.com/akkoro/deploy-a-jamstack-site-on-aws-lambda-with-api-gateway-in-10-minutes-or-less-kb0</link>
      <guid>https://forem.com/akkoro/deploy-a-jamstack-site-on-aws-lambda-with-api-gateway-in-10-minutes-or-less-kb0</guid>
      <description>&lt;p&gt;&lt;a href="https://jamstack.org" rel="noopener noreferrer"&gt;Jamstack&lt;/a&gt; architecture is all the rage these days, owing to its focus on high performance and scalability. With an emphasis on serving pre-generated content, for some it may feel a little bit like a return to the old days. The underlying infrastructure however is anything but!&lt;/p&gt;

&lt;p&gt;This article will walk through building and deploying a simple static site, served using &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; and &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt;. To accomplish this we will use &lt;a href="https://assemblylift.akkoro.io" rel="noopener noreferrer"&gt;AssemblyLift&lt;/a&gt;, an open-source platform designed to quickly &amp;amp; easily accomplish such a task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;You will need an &lt;a href="https://portal.aws.amazon.com/billing/signup#/start" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; if you do not have one already. The small application you will build here fits comfortably in the free tier. 🙂&lt;/p&gt;

&lt;p&gt;You will also need the &lt;a href="https://rustup.rs/" rel="noopener noreferrer"&gt;Rust toolchain&lt;/a&gt; and &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;NPM&lt;/a&gt; installed. In addition you will need to install the &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; build target for the Rust toolchain (&lt;code&gt;rustup toolchain install wasm32-unknown-unknown&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Once you have these prerequisites you can install AssemblyLift with &lt;code&gt;cargo install assemblylift-cli&lt;/code&gt;. Run &lt;code&gt;asml help&lt;/code&gt; to verify the installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Jamstack project
&lt;/h2&gt;

&lt;p&gt;We will base our application on the AssemblyLift project template available &lt;a href="https://github.com/akkoro/assemblylift-template-jamstack" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;. You can clone this, or click the "use this template" button to create a new repo in your account from the template.&lt;/p&gt;

&lt;p&gt;The template project includes a simple static site that we will build with Webpack. It also includes an AssemblyLift service called &lt;code&gt;www&lt;/code&gt; which implements our server function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project structure
&lt;/h3&gt;

&lt;p&gt;Let's take a closer look at the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── README.md
├── assemblylift.toml
├── babel.config.json
├── package-lock.json
├── package.json
├── services
│   └── www
│       ├── server
│       │   ├── Cargo.lock
│       │   ├── Cargo.toml
│       │   └── src
│       │       └── lib.rs
│       └── service.toml
├── web
│   ├── images
│   │   └── AssemblyLift_logo_with_text.png
│   ├── main.js
│   ├── style
│   │   └── main.css
│   └── views
│       └── index.ejs
└── webpack.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project root contains configuration files for AssemblyLift, Babel, NPM, and Webpack. There is also the AssemblyLift &lt;code&gt;services&lt;/code&gt; directory, and a directory called &lt;code&gt;web&lt;/code&gt; which we've chosen for our frontend code. Feel free to rename the &lt;code&gt;web&lt;/code&gt; directory if you like, just don't forget to update the Webpack config accordingly!&lt;/p&gt;

&lt;p&gt;This project has one service &lt;code&gt;www&lt;/code&gt; containing one function &lt;code&gt;server&lt;/code&gt; (sometimes written &lt;code&gt;www/server&lt;/code&gt; or &lt;code&gt;www.server&lt;/code&gt;). The server function is a Rust crate containing the function handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  A closer look
&lt;/h2&gt;

&lt;p&gt;Next let's look at the handler function in &lt;code&gt;www/server&lt;/code&gt;. You won't need to change it for this walkthrough, but it will be helpful to understand how it works if you want to expand on it yourself 🙂.&lt;/p&gt;

&lt;h3&gt;
  
  
  The code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;asml_awslambda&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;flate2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;write&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GzEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;flate2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Compression&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;mime_guess&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rust_embed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RustEmbed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_awslambda&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;asml_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GuestCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;handler!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LambdaContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApiGatewayEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="py"&gt;.event.path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nn"&gt;AwsLambdaClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;console_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Serving {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;PublicAssets&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;GzEncoder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;Compression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nn"&gt;mime_guess&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                    &lt;span class="nf"&gt;.first_or_octet_stream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asset&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gzip&lt;/span&gt;&lt;span class="nf"&gt;.finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="nd"&gt;http_ok!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// true, true: we always gzip &amp;amp; encode base64&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;http_not_found!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(RustEmbed)]&lt;/span&gt;
&lt;span class="nd"&gt;#[folder&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"../../../dist/"&lt;/span&gt;&lt;span class="nd"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PublicAssets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;p&gt;Most of the heavy lifting in this function is done by &lt;a href="https://crates.io/crates/rust-embed" rel="noopener noreferrer"&gt;&lt;code&gt;rust-embed&lt;/code&gt;&lt;/a&gt;, which embeds the contents of &lt;code&gt;dist&lt;/code&gt; (our Webpack output directory) in the compiled binary. This allows our static assets to be deployed to Lambda bundled inside the function code, as long as the resulting binary is less than 50MB.&lt;/p&gt;

&lt;p&gt;The rest of the function code deals mainly with API Gateway. The &lt;code&gt;path&lt;/code&gt; received by the function is matched against the embedded assets; if one is found, it is gzipped and then encoded base64 as required by API Gateway to serve binary content. The &lt;code&gt;content-type&lt;/code&gt; header is set using the &lt;code&gt;mime_guess&lt;/code&gt; crate, defaulting to &lt;code&gt;application/octet-stream&lt;/code&gt; if type cannot be guessed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure, build, deploy
&lt;/h2&gt;

&lt;p&gt;Technically you can deploy the template as-is without any configuration, but you will likely want to at least rename the project. To do this look at &lt;code&gt;assemblylift.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"assemblylift-template-jamstack"&lt;/span&gt;

&lt;span class="c"&gt;# In production you should configure an S3 bucket &amp;amp; DynamoDB table for terraform state&lt;/span&gt;
&lt;span class="c"&gt;#[terraform]&lt;/span&gt;
&lt;span class="c"&gt;#state_bucket_name = "my-tf-state"&lt;/span&gt;
&lt;span class="c"&gt;#lock_table_name = "my-tf-lock"&lt;/span&gt;

&lt;span class="nn"&gt;[services]&lt;/span&gt;
&lt;span class="py"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"www"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about the AssemblyLift TOML documents on the &lt;a href="https://docs.assemblylift.akkoro.io/learn-assemblylift/services" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;, however this is where you would set the name of the project (included in the names of the created AWS resources) as well as add more services if you want. There is also a block for configuring Terraform remote state storage, which is highly recommended for use in production!&lt;/p&gt;

&lt;p&gt;To build an AssemblyLift application you use the &lt;code&gt;asml cast&lt;/code&gt; command; this will compile everything which needs to be deployed and store it immutably in the &lt;code&gt;net&lt;/code&gt; directory. &lt;/p&gt;

&lt;p&gt;If you haven't built the Webpack project however, you will get an error! Build your web assets first with &lt;code&gt;npm run build&lt;/code&gt;. Once they are available in &lt;code&gt;dist&lt;/code&gt;, your function code should build no problem.&lt;/p&gt;

&lt;p&gt;Finally to deploy our service! AssemblyLift applications are deployed with &lt;code&gt;asml bind&lt;/code&gt;. This process delegates to Terraform and will require appropriate AWS credentials to be available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the server function
&lt;/h2&gt;

&lt;p&gt;Unfortunately you'll have to leave the command line for the next part, as we'll need to access the AWS console to find our new service URL. Ideally in a future release the Asml command line will do more of this for you :).&lt;/p&gt;

&lt;p&gt;First navigate to the API Gateway section of the AWS Console. You should see an API by the name you gave above listed here.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq37i0iwt2dkwetcih5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq37i0iwt2dkwetcih5q.png" alt="A screenshot of the API Gateway console showing a single API listed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the listed API to view more details.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fczwzdaavs8sdtu2ow7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fczwzdaavs8sdtu2ow7.png" alt="A screenshot of the API Gateway console showing the listed API stages"&gt;&lt;/a&gt;&lt;br&gt;
This is where each API &lt;em&gt;stage&lt;/em&gt; is listed; AssemblyLift always uses the default stage. The URL is the endpoint used to invoke your API.&lt;/p&gt;

&lt;p&gt;Our API defines only one route, &lt;code&gt;/{path+}&lt;/code&gt; which is called a &lt;em&gt;proxy path&lt;/em&gt;. This instructs API Gateway to take the entire path as a variable called &lt;code&gt;path&lt;/code&gt;. Our server function will map the path &lt;code&gt;/&lt;/code&gt; to &lt;code&gt;/index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you click on the URL, you should get a very simple static site with the AssemblyLift logo returned back to you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;The first thing you'll want to do of course is replace the template site with your own. You can start by just replacing the content, but you're not obligated to keep using Webpack! This function should be compatible with any framework or site generator that ends with a directory of static assets. &lt;/p&gt;

&lt;p&gt;If you paid attention to the function code above, it might have occurred to you that the assets we serve don't have to be completely static. For example we could instead embed a handlebars template or similar &amp;amp; render it with the &lt;a href="https://crates.io/crates/handlebars" rel="noopener noreferrer"&gt;&lt;code&gt;handlebars&lt;/code&gt;&lt;/a&gt; crate, allowing us "semi-dynamic" content for lack of a better name.&lt;/p&gt;

&lt;p&gt;You may also want to create a CloudFront distribution for your API. I have found performance adequate without a CDN, however it will likely improve with one. Putting a CDN in front of the function should also help hide or smooth out latency due to cold start time, if the function is not invoked regularly. CloudFront is not expensive, however whether it is worth it will be up to you 🙂.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;The best way to learn about AssemblyLift right now is by visiting the &lt;a href="https://docs.assemblylift.akkoro.io" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also reach out to us on &lt;a href="https://twitter.com/akkorocorp" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, via &lt;a href="https://element.io" rel="noopener noreferrer"&gt;Element&lt;/a&gt; at #assemblylift:matrix.org, or in the comments below!&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>aws</category>
      <category>rust</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AssemblyLift v0.3 Released</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Wed, 04 Aug 2021 16:42:45 +0000</pubDate>
      <link>https://forem.com/akkoro/assemblylift-v0-3-released-3p3</link>
      <guid>https://forem.com/akkoro/assemblylift-v0-3-released-3p3</guid>
      <description>&lt;p&gt;After several months in development, AssemblyLift v0.3 has been released and made available via Cargo! 🥳&lt;/p&gt;

&lt;p&gt;If you've never heard of it or just need a reminder, &lt;a href="https://assemblylift.akkoro.io"&gt;AssemblyLift&lt;/a&gt; is an open platform for developing cloud-native applications. At its core are the AssemblyLift Runtime and the AssemblyLift CLI. The runtime provides an environment for executing compiled WebAssembly (WASM) on infrastructure such as AWS Lambda, and is one of the more unique aspects of the platform which we'll dig into more below. &lt;/p&gt;

&lt;p&gt;If you'd like to install the CLI and explore the things being discussed in this post, get started by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cargo install assemblylift-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the CLI is currently supported on Linux and macOS only.&lt;/p&gt;

&lt;h2&gt;
  
  
  State of the World
&lt;/h2&gt;

&lt;p&gt;There's an adage I can't remember the origin of, which states that something must be built three times in order to get it right. On the first pass you are learning what you wish to build. On the second, you are learning how to build it. Then on the third, you build it for real.&lt;/p&gt;

&lt;p&gt;It's never quite that simple of course (the road to 1.0 is long), but much of the work that has gone into the v0.3 release is "beneath the surface", making this a rather foundational release so to speak. Lots of key areas were reworked or completely rewritten based on the lessons of v0.1 &amp;amp; v0.2, which make implementing new features easier now and in the future.&lt;/p&gt;

&lt;p&gt;That said, there are also many user-facing improvements which make this the most "complete" offering of AssemblyLift to date!&lt;/p&gt;

&lt;p&gt;Below we'll highlight the major changes between v0.2 and v0.3, and walk through each in more detail in the following sections. Towards the end, I'll discuss a little more about where the project is headed next. 🙂&lt;/p&gt;

&lt;h3&gt;
  
  
  Change List
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Support multiple infrastructure providers for services &amp;amp; functions

&lt;ul&gt;
&lt;li&gt;Experimental Lambda runtime based on Alpine Linux&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;New TOML-&amp;gt;HCL transpiler pipeline&lt;/li&gt;
&lt;li&gt;Support for arbitrarily large data buffers which can be paged into WASM memory (function input and IOmod responses)&lt;/li&gt;
&lt;li&gt;Deployment of &amp;amp; support for the IO Module Registry&lt;/li&gt;
&lt;li&gt;Bump critical dependencies to latest versions! In particular, Tokio 1.x is now used throughout the Asml crate ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Infrastructure Providers
&lt;/h3&gt;

&lt;p&gt;I'll be honest; this is the change which took the most work and is easily the least visible 😂&lt;/p&gt;

&lt;p&gt;But! That doesn't mean it wasn't important.&lt;/p&gt;

&lt;p&gt;While AWS Lambda is still the de facto provider, a goal of the project has always been to support deployment to a variety of kinds of infrastructure.&lt;/p&gt;

&lt;p&gt;To accomplish this, most of the code which translated your AssemblyLift TOML into Terraform HCL needed to be thrown away and rewritten. This resulted in the new transpiler mentioned above!&lt;/p&gt;

&lt;p&gt;The new provider implementation was tested with an experimental provider for Lambda based on Alpine Linux. The original purpose for this provider was to support faster, LLVM-compiled WASM binaries which are incompatible with the default Lambda OS. Unfortunately, despite the tiny image size I discovered that cold-start time a for Docker-based Lambda is much too high to be worth it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Buffers &amp;amp; Memory Management
&lt;/h3&gt;

&lt;p&gt;A well-published feature of WebAssembly is its memory model, which is a sandboxed linear memory. This essentially means that WASM code has a large array of bytes to work with inside the sandbox, but there are no pointers or other means of escaping elsewhere. A side-effect of this is that to get new data into WebAssembly from the host, the host must &lt;em&gt;write into&lt;/em&gt; a known region of the linear memory.&lt;/p&gt;

&lt;p&gt;In previous versions, AssemblyLift did not buffer data on the host before passing it to the WASM guest. If the data could not all fit in the defined region within the guest, tough luck!&lt;/p&gt;

&lt;p&gt;Clearly that's no good. So there are now host-side buffers which can reach arbitrary size, and then be paged into a location in WASM via ABI calls as needed. This change shows up both on function input, as well as in IOmod memory management as responses are buffered and handled by the guest.&lt;/p&gt;

&lt;h3&gt;
  
  
  IO Module Registry
&lt;/h3&gt;

&lt;p&gt;Another biggin' that has been in-progress for an age.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.assemblylift.akkoro.io/learn-assemblylift/io-modules"&gt;IO Modules&lt;/a&gt; or &lt;em&gt;IOmods&lt;/em&gt; are a unique feature of AssemblyLift which are used to provide the WASM guest with access to outside functionality.&lt;/p&gt;

&lt;p&gt;As of v0.3 the CLI can fetch IOmod dependencies from the new public IOmod Registry, while IOmods themselves are now shipped in Zip-archived packages. As of writing, the &lt;a href="https://docs.assemblylift.akkoro.io/learn-assemblylift/io-modules/registry"&gt;modules available on the registry&lt;/a&gt; are limited, but more are planned (including modules for all remaining AWS services).&lt;/p&gt;

&lt;h2&gt;
  
  
  Upcoming Improvements
&lt;/h2&gt;

&lt;p&gt;The next point release will focus on improving quality &amp;amp; error handling in a few areas of the code, as well as improving the DX* around the API Gateway/HTTP macros. At the moment for example, the &lt;code&gt;http_ok!&lt;/code&gt; macro expects your response to be JSON. This suits API applications fine, but we would like to be more flexible and to support returning HTML from our functions. This opens us up to full-stack development with AssemblyLift in the future! 😎&lt;/p&gt;

&lt;p&gt;In the longer term, we'll be expanding what can be managed by AssemblyLift directly. For example function permissions and/or IAM policies can not be set in the service TOML at the moment, which is a major usability issue that should be addressed. &lt;/p&gt;

&lt;p&gt;We'll also be evaluating additional providers to support in addition to AWS Lambda.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;* Developer eXperience&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;The best way to learn about AssemblyLift right now is by visiting the &lt;a href="https://docs.assemblylift.akkoro.io"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also reach out to us on &lt;a href="https://twitter.com/akkorocorp"&gt;Twitter&lt;/a&gt;, via &lt;a href="https://element.io"&gt;Element&lt;/a&gt; at #assemblylift:matrix.org, or in the comments below!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>rust</category>
      <category>webassembly</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
