AssemblyScript on Compute@Edge

WARNING: This information is part of a beta release, which may be subject to breaking changes and improvements over time. For more information, see our product and feature lifecycle descriptions.

Compute@Edge supports application code written in AssemblyScript which is a strict subset of TypeScript that compiles to Wasm. It is a great SDK to get started with on Compute@Edge if you are used to writing traditional JavaScript or TypeScript applications, as it combines the familiarity of TypeScript syntax with the performance and reliability guarantees of strict typing and low-level control.

Project layout

If you don't yet have a working toolchain and Compute@Edge service set up, start by getting set up.

At the end of the initialization process, the current working directory will contain a file tree resembling the following:

├── README.md
├── fastly.toml
├── node_modules
├── package-lock.json
├── package.json
└── assembly
└── index.ts

The most important file to work on is assembly/index.ts, which contains the logic you'll run on incoming requests. If you initialized your project from the default starter template, the contents of this file should match the one in the template's repo. The other files include:

  • npm metadata: package.json and package-lock.json describe the dependencies of your package, managed using npm, Node's package manager.
  • Fastly metadata: The fastly.toml file contains metadata required by Fastly to deploy your package to a Fastly service. It is generated by the init command and for the moment, should not be edited manually.
  • Project dependencies: The node_modules directory contains the dependencies of your package.

Main interface

Unlike Rust, there is no requirement for a main() function in an AssemblyScript program.

The program will be invoked for each request that Fastly receives for a domain attached to your service, and it must call Fastly.respondWith with a valid response to send to the client. You can fetch the downstream Request object by calling Fastly.getClientRequest().

assembly/index.ts
AssemblyScript
3// Get the request from the client.
4let req = Fastly.getClientRequest();
5
6// Send the request to the backend server.
7let beresp = Fastly.fetch(req, {
8 backend: "example_backend",
9 cacheOverride: null,
10}).wait();
11
12// Send the backend response back to the client.
13Fastly.respondWith(beresp);

The @fastly/as-compute module provides the core Fastly, Request, and Response types referenced in this guide.

Communicating with backend servers and the Fastly cache

A Request can be forwarded to any backend defined on your service. Backends can be created via the Fastly CLI, API, or web interface, and are referenced by name. If you specify a backend hostname as part of completing the fastly compute deploy wizard, it will be named the same as the hostname or IP address, but with . replaced with _ (e.g., 123_456_789_123). It's a good idea to define backend names as constants:

const backendName = "my_backend_name";

And then reference them when you want to forward a request to a backend:

assembly/index.ts
AssemblyScript
13// Send the request to the backend server.
14let beresp = Fastly.fetch(req, {
15 backend: backendName,
16 cacheOverride,
17}).wait();

Requests forwarded to a backend will transit the Fastly cache, and the response may come from cache. Where a request doesn't find a matching result in cache, it will be sent to the origin, and the response will be cached based on the freshness rules determined from its HTTP response headers (unless overridden, as in the example above, by Fastly.CacheOverride).

In a future release, it will be possible to interact with the cache and the network separately.

Composing requests and responses

In addition to the request returned by Fastly.getClientRequest and responses returned from Fastly.fetch, requests and responses can also be constructed. This is useful if you want to make an arbitrary API call that is not derived from the client request, or if you want to make a response to the client without making any backend fetch at all.

To compose a request from scratch, instantiate a new Request:

// Create some headers for our upstream request to our origin.
let upstreamHeaders = new Headers();
upstreamHeaders.set("some-header", "someValue");
// Create our upstream request to our origin using our upstream headers.
let bereq = new Request("https://example.com/", {
method: "POST",
headers: upstreamHeaders,
body: null
});

Similarly, responses can be created by instantiating a Response:

assembly/index.ts
AssemblyScript
6// Set some basic headers.
7let headers = new Headers();
8headers.set('Content-Type', 'text/plain');
9
10let resp = new Response(String.UTF8.encode("Hi from the edge"), {
11 status: 200,
12 headers,
13 url: req.url
14})

Using edge dictionaries

Fastly allows you to configure edge dictionaries on your Compute@Edge services. These can be accessed using the Fastly.Dictionary interface in the SDK.

assembly/index.ts
AssemblyScript
3let exampleDictionary = new Fastly.Dictionary("example_dictionary");
4let someValue = "";
5if (exampleDictionary.contains("key_name")) {
6 let valueOrNull = exampleDictionary.get("key_name");
7 if (valueOrNull != null) {
8 someValue = changetype<string>(valueOrNull);
9 }
10}

Logging

Fastly.LogEndpoint provides a standardized interface for sending logs to Fastly real-time logging, which can be attached to many third party logging providers. Before adding logging code to your Compute@Edge program, set up your log endpoint using the CLI, API, or web interface. Log endpoints are referenced in your code by name:

let logEndpoint = Fastly.getLogEndpoint("AssemblyscriptLog");
logEndpoint.log("Hello!");
// logs "Hello!" to the "AssemblyScriptLog" log endpoint

If your code errors, output will be emitted to stderr:

throw new Error('Oh no!');
// logs "abort: Oh no! in your/file.ts(line:col)" to stderr

Using dependencies

Compute@Edge compiles your code to WebAssembly and uses the WebAssembly System Interface (WASI). Because of this, it supports WASI-compatible npm modules. Our Fiddle tool allows the use of a subset of modules that we have tested and confirmed will work with Compute@Edge:

Access to the client request, creating requests to backends, the Fastly cache, and other Fastly features are exposed via Fastly's own public modules:

  • @fastly/as-compute - Core interfaces, provides access to client request, backend fetches and caching

For access to more APIs, these publically available modules work great with Compute@Edge:

  • as-wasi - Implementations for date, time, random number generation, etc. as a layer on top of the WASI interface.
  • assemblyscript-regex - A Regex implementation for AssemblyScript, meant to closely follow the JavaScript RegExp class.
  • assemblyscript-json - JSON encoder / decoder for AssemblyScript.
  • as-inliner - An AssemblyScript transform that allows you to embed the contents of a file as a string or StaticArray in an AssemblyScript module.

Testing and debugging

Logging is the main mechanism to debug Compute@Edge programs. Log output from live services can be monitored via live log tailing. The local test server and Fastly Fiddle display all log output automatically. See Testing & debugging for more information about choosing an environment in which to test your program.

You may choose to write unit tests for small independent pieces of your AssemblyScript code intended for Compute@Edge. However, Compute@Edge apps heavily depend on and interact with Fastly features and your own systems. This can make an integration testing strategy that focusses on a lesser number of high impact tests more valuable.