AssemblyScript on Compute

WARNING: Fastly's support for AssemblyScript is deprecated. We recommend using our JavaScript SDK instead.

AssemblyScript is a strict subset of TypeScript that compiles to Wasm.

Project layout

If you don't yet have a working toolchain and Compute 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 repository. 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

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
4
5
6
7
8
9
10
11
12
13
// Get the request from the client.
let req = Fastly.getClientRequest();
// Send the request to the backend server.
let beresp = Fastly.fetch(req, {
backend: "example_backend",
cacheOverride: null,
}).wait();
// Send the backend response back to the client.
Fastly.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., 151_101_129_57). 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
14
15
16
17
// Send the request to the backend server.
let beresp = Fastly.fetch(req, {
backend: backendName,
cacheOverride,
}).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
7
8
9
10
11
12
13
14
// Set some basic headers.
let headers = new Headers();
headers.set('Content-Type', 'text/plain');
let resp = new Response(String.UTF8.encode("Hi from the edge"), {
status: 200,
headers,
url: req.url
})

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 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 compiles your code to WebAssembly and uses the WebAssembly System Interface (WASI). Because of this, it supports WASI-compatible npm modules. Access to the client request, creating requests to backends, the Fastly cache, and other Fastly features are exposed via Fastly's own public module:

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

Testing and debugging

Logging is the main mechanism to debug Compute 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. However, Compute apps heavily depend on and interact with Fastly features and your own systems. This can make an integration testing strategy that focuses on a lesser number of high impact tests more valuable.