---
title: JavaScript on the Compute platform
summary: null
url: >-
  https://www.fastly.com/documentation/guides/compute/developer-guides/javascript
---

The Compute platform supports application code written in JavaScript. The SDK supports both JavaScript and TypeScript source files and produces a [WebAssembly (Wasm)](https://webassembly.org/) binary.

It is a great SDK to get started with on the platform if you are used to writing browser-based JavaScript or Node.js applications.

The Compute JavaScript runtime is designed to be compliant with [JavaScript standards](https://ecma-international.org/publications-and-standards/standards/ecma-262/) and the [Minimum Common Web APIs](https://min-common-api.proposal.wintertc.org) (defined by the [WinterTC](https://wintertc.org)). It uses web platform APIs such as the [Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) and [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), enabling it to interoperate with other modern JavaScript code.

## Quick access

- [SDK reference](https://js-compute-reference-docs.edgecompute.app/)
- [Code examples](/solutions/examples/javascript/)
- [Starter kit](/solutions/starters/compute-starter-kit-javascript-default/)

> **HINT:** If you are using a JavaScript web framework such as Gatsby, Next.js, or Remix, check out [using frameworks on the Compute platform](https://www.fastly.com/documentation/guides/compute/developer-guides/frameworks).

## Project layout

Development for Fastly Compute in JavaScript involves Node.js for developer tooling and dependency management (Node.js is not involved in the execution of the built Fastly Compute application).

If you don't yet have Node.js and a Compute service set up, start by [getting set up](https://www.fastly.com/documentation/guides/compute/getting-started-with-compute/).

> **HINT:**
> For an alternative method to initialize your Compute application with JavaScript or TypeScript, use npm with the `@fastly/compute` initializer to interactively initialize your application.
>
> ```term
> $ mkdir example && cd example
> $ npm create @fastly/compute@latest
> ```
>
> See the `@fastly/create-compute` initializer's [reference page](https://www.npmjs.com/package/@fastly/create-compute) for full options (e.g., if you want to use a directory other than the current one).

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

```plain lineNumbers=false nocopy
├── src
│   └── index.js
├── fastly.toml
├── package.json
└── README.md
```

The most 

- `package.json`: your project's package metadata, managed using [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager), Node's package manager. Includes important information such as the name, module type, and dependencies.
  - We recommend setting `"type"` to `"module"`, enabling your project to use the modern [ES module format](https://nodejs.org/api/esm.html).
  - We recommend setting `"private"` to `true`, so that the project doesn't accidentally get published as a package.
- `fastly.toml`: metadata required by Fastly to deploy your package to a Fastly service. It is generated by the <kbd>fastly compute init</kbd> command. [Learn more about `fastly.toml`](https://www.fastly.com/documentation/reference/compute/fastly-toml/).

> **IMPORTANT:** Some starter kits may include a `webpack` configuration or other module bundling tooling. If you are using `webpack` or similar tools, you may need to include rules to ensure that Fastly's namespaced imports (`fastly:*`) work correctly. Learn more about [module bundling](https://www.fastly.com/documentation/guides/compute/developer-guides/javascript#module-bundling).

## Main interface

Incoming requests trigger an [event handler](https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers) function with a `fetch` event. This may look familiar if you've used the [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API).

A [`FetchEvent`](https://js-compute-reference-docs.edgecompute.app/docs/globals/FetchEvent/) will be dispatched for each request that Fastly receives that's [routed to your service](https://www.fastly.com/documentation/guides/concepts/routing-traffic-to-fastly/), and any associated [`EventListeners`](https://developer.mozilla.org/en-US/docs/Web/API/EventListener) must synchronously call [`event.respondWith`](https://js-compute-reference-docs.edgecompute.app/docs/globals/FetchEvent/prototype/respondWith) with a valid response to send to the client. The [`FetchEvent`](https://js-compute-reference-docs.edgecompute.app/docs/globals/FetchEvent/)'s `.request` property is a standard web [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), while `.client` exposes data about the requesting client.

Although `event.respondWith` must be called synchronously, the argument provided to it may be a Promise, so it is often convenient to define an `async` function to handle the request and return a promised `Response`. This pattern is generally the best way to get started writing a Compute program in JavaScript:

```js title="src/index.js" linenums
/// <reference types="@fastly/js-compute" />

// section visible
addEventListener("fetch", event => event.respondWith(handleRequest(event)) );

async function handleRequest(event) {
  // Get the request from the client.
  const req = event.request;

  return fetch(req, {
    backend: "example_backend"
  });
}
// section-end visible
```

The [`FetchEvent`](https://js-compute-reference-docs.edgecompute.app/docs/globals/FetchEvent/) is provided by the [`@fastly/js-compute`](https://js-compute-reference-docs.edgecompute.app) module, Fastly's JavaScript SDK, which is included in your project's [dependencies](https://www.fastly.com/documentation/guides/compute/developer-guides/javascript#using-dependencies) automatically when you run the <kbd>fastly compute init</kbd> command.

## Communicating with backend servers and the Fastly cache

You can make HTTP requests from your Fastly service by passing a `Request` to the [`fetch()`](https://js-compute-reference-docs.edgecompute.app/docs/globals/fetch) function. Our implementation of `fetch` offers a few extra properties compared to the web standard version, including `.backend`, which allows you to specify the name of a [backend](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends) defined statically on your service, or pass an instance of a [dynamic backend](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends/#dynamic-backends). If you specify a backend hostname as part of completing the <kbd>fastly compute deploy</kbd> 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:

```js
const backendName = "my_backend_name";
```

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

```js title="src/index.js" linenums
/// <reference types="@fastly/js-compute" />

// section visible

const backendName = "my_backend_name";

function handler(event) {
  // section visible
  // Create a cache override.
  let cacheOverride = new CacheOverride({ ttl: 60 });

  return fetch(event.request, {
    backend: backendName,
    cacheOverride
  });
}
// section-end visible

addEventListener("fetch", event => event.respondWith(handler(event)));
```

If using [dynamic backends](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends#dynamic-backends), optionally call `allowDynamicBackends()` to automatically create backends on demand from the properties of the `Request`:

```js
/// <reference types="@fastly/js-compute" />

async function app() {
  // For any request, return the fastly homepage -- without defining a backend!
  return fetch('https://www.fastly.com/');
}
addEventListener("fetch", event => event.respondWith(app(event)));
```

> **HINT:** Many JavaScript libraries expect to use the standard `fetch` API to make HTTP requests. If your application imports a dependency that makes HTTP calls using `fetch`, those requests will fail unless [dynamic backends](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends#dynamic-backends) are enabled on your account.

Requests forwarded to a backend will typically transit the Fastly readthrough cache interface, and the response may come from cache. See [Readthrough (HTTP) cache](https://www.fastly.com/documentation/guides/concepts/cache#readthrough-cache) for more precise or explicit control over the Fastly readthrough cache.

### Customizing cache behavior

<div id="advanced-caching" />

> **JavaScript SDK:** Customizing cache behavior with the readthrough (HTTP) cache is an opt-in feature; enable it by adding the `--enable-http-cache` flag to the `js-compute-runtime` command used to build your application (usually the `"build"` script of `package.json`):
> ```sh
> js-compute-runtime --enable-http-cache ./src/index.js ./bin/main.wasm
> ```

The readthrough cache interface can be further customized in the way it interacts with the backend, supporting use cases such as:

- making modifications to a request only when it is forwarded to a backend
- reading and modifying response status code and headers, and adjusting cache controls
- transforming the body of the response that is stored into the cache

See [Customizing cache interaction with the backend](https://www.fastly.com/documentation/guides/concepts/cache#customizing-cache-interaction-with-the-backend) for details.

> **HINT:** Check out the [Advanced caching starter kit for JavaScript](https://www.fastly.com/documentation/solutions/starters/compute-starter-kit-javascript-advanced-caching/), which includes some working examples of this feature and can be used as a starting point for your application.

## Composing requests and responses

In addition to the `Request` referenced by `event.request` and `Response` objects returned from the `fetch()` function, 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`](https://js-compute-reference-docs.edgecompute.app/docs/globals/Request/):

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  // Create some headers for the request to origin
  let upstreamHeaders = new Headers({ "some-header": "someValue" });

  // Create a POST request to our origin using the custom headers
  let upstreamRequest = new Request("https://example.com/", {
    method: "POST",
    headers: upstreamHeaders,
  });
  // section-end visible

  return fetch(upstreamRequest, {
    backend: "example_backend",
  });
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

Similarly, responses can be created by instantiating a [`Response`](https://js-compute-reference-docs.edgecompute.app/docs/globals/Response/):

```js title="src/index.js" linenums
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  const headers = new Headers();
  headers.set('Content-Type', 'text/plain');

  return new Response("Hi from the edge", {
    status: 200,
    headers,
    url: event.request.url
  })
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

## Parsing and transforming responses

Requests and responses in the Compute platform are [streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), which allows large payloads to move through your service without buffering or running out of memory. Conversely, running methods such as [`text`](https://js-compute-reference-docs.edgecompute.app/docs/globals/Response/prototype/text) on a `Response` will force the stream to be consumed entirely into memory. This can be appropriate where a response is known to be small or needs to be complete to be parsable.

Parsing JSON responses is available natively in the Fetch API via the [`json()`](https://js-compute-reference-docs.edgecompute.app/docs/globals/Response/prototype/json) method of a `Response` but also requires consuming the entire response into memory:

```js
/// <reference types="@fastly/js-compute" />

async function handler(event) {
  // section visible
  let backendResponse = await fetch("https://example-backend-host/api/checkAuth", {
    method: "POST",
    backend: "example_backend",
  });

  // Take care! .json() will consume the entire body into memory!
  let jsonData = await backendResponse.json();
  jsonData.newProperty = "additional data";

  return new Response(
    JSON.stringify(jsonData),
    { headers: { "Content-Type": "application/json" } }
  );
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

However, it is better to avoid buffering responses, especially if the response is large, being delivered slowly in multiple chunks, or capable of being rendered progressively by the client. The Fastly JavaScript SDK implements [WHATWG streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API). In this example, the backend response is capitalized as it's received, and each chunk is passed on to the client once it has been transformed:

```js
/// <reference types="@fastly/js-compute" />

// Create a transform stream that uppercases text
class UppercaseTransform extends TransformStream {
  constructor() {
    super({
      transform: (chunk, controller) => {
        const chunkStr = this.textDecoder.decode(chunk);
        const transformedChunkStr = chunkStr.toUpperCase();
        const outputBytes = this.textEncoder.encode(transformedChunkStr)
        controller.enqueue(outputBytes);
      }
    });
    this.textEncoder = new TextEncoder();
    this.textDecoder = new TextDecoder();
  }
}

async function handler(event) {
  const clientReq = event.request;
  clientReq.headers.delete('accept-encoding');
  const backendResponse = await fetch(clientReq, { backend: "example_backend" });

  // Pass the backend response through a filter, which uppercases all the text.
  const newBodyStream = backendResponse.body.pipeThrough(new UppercaseTransform());

  // Construct a response using the filtered stream and deliver it to the client.
  return new Response(newBodyStream, {
    headers: {
      ...backendResponse.headers,
      "cache-control": "private, no-store"
    }
  });
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

### Compression

Fastly can compress and decompress content automatically, and it is often easier to use these features than to try to perform compression or decompression within your JavaScript code. Learn more about [compression with Fastly](https://www.fastly.com/documentation/guides/concepts/compression).

## Performance Optimizations

To enable a series of compiler optimizations that can yield a [~3x faster JavaScript performance on Compute](https://www.fastly.com/blog/speeding-up-javascript-on-compute), update to the latest version of the SDK (v3.27.0 or greater) and add `–-enable-aot` to [the build flags in your project’s package.json file](https://github.com/fastly/compute-starter-kit-javascript-default/blob/1132b8c4f92e752519f65439c551d75d61cb70d6/package.json#L15). We recommend testing the changes before rolling them out.

For example, if your build flags looked like this before:

`"build": "js-compute-runtime bin/index.js bin/main.wasm",`

The resulting change would look like this:

`"build": "js-compute-runtime --enable-aot bin/index.js bin/main.wasm",`

## Using edge data

Fastly allows you to configure various forms of data stores to your services, both for dynamic configuration and for storing data at the edge. The JavaScript SDK exposes the [`fastly:config-store`](https://js-compute-reference-docs.edgecompute.app/docs/fastly:config-store/ConfigStore/), [`fastly:kv-store`](https://js-compute-reference-docs.edgecompute.app/docs/fastly:kv-store/KVStore/), and `fastly:secret-store`(<https://js-compute-reference-docs.edgecompute.app/docs/fastly:secret-store/SecretStore/>) packages to allow access to these APIs.

All edge data resources are account-level, [service-linked resources](https://www.fastly.com/documentation/guides/compute/edge-data-storage/about-edge-data-stores/), allowing a single store to be accessed from multiple Fastly services.

## Environment variables

Compute services can access a set of defined environment variables provided by the system at runtime as well as custom environment variables you specify at build-time. See [environment variables for the Compute platform](https://www.fastly.com/documentation/reference/compute/ecp-env/) for details.

## Logging

[`console`](https://js-compute-reference-docs.edgecompute.app/docs/globals/console/log) provides a standardized interface for emitting log messages to STDOUT or STDERR.

To send logs to [Fastly real-time logging](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-logging/), which can be attached to many third-party logging providers, use the `Logger` class. Log endpoints are referenced in your code by name:

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {

  // logs "Hello!" to the "JavaScriptLog" log endpoint
  const logger = new Logger("JavaScriptLog");
  logger.log("Hello!");

  return new Response({ status: 200 });
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

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

```js
/// <reference types="@fastly/js-compute" />

function handler(event) {
  // section visible
  // This logs "abort: Oh no! in src/index.js(line:col)" to stderr.
  throw new Error('Oh no!');
  // section-end visible
}

addEventListener("fetch", event => event.respondWith(handler(event)));
```

## Using dependencies

The Compute JavaScript SDK is designed to follow [Node.js behavior for handling packages](https://nodejs.org/api/packages.html) and supports the installation of dependency packages using the [`npm install` command](https://docs.npmjs.com/cli/v8/commands/npm-install).

A JavaScript application for Compute must include Fastly's public package [`@fastly/js-compute`](https://www.npmjs.com/package/@fastly/js-compute) as a dependency as it includes the JavaScript runtime enabling the application to execute within and interact with the Fastly platform.

An application may depend on additional packages obtained through [the npm repository](https://www.npmjs.com) or any other compatible channel. The Compute JavaScript platform supports [WinterTC](https://wintertc.org)-compatible npm modules, which in practice are modules that don't depend on any specific platform's bindings at runtime (outside Fastly Compute).

Our [Fiddle](https://www.fastly.com/documentation/reference/tools/fiddle) tool allows the use of an approved subset of modules tested for compatibility with the Compute platform. As a lightweight experimentation environment, Fiddle supports core functionality from these modules, though not all features of every module may be supported.

> **NOTE:** This page contains a formatted list. Visit the URL to see the full content.

This is a tiny fraction of the modules that will work on the Compute platform, but are the most commonly useful modules for experimentation when building applications.

## Developer experience

For the best experience of developing for the Compute platform in JavaScript, include the following comment at the top of any file that works with Fastly objects or `fastly:` namespaced modules:

```javascript
/// <reference types="@fastly/js-compute" />
```

This will allow your IDE to import the type definitions for the Fastly JavaScript SDK. If you use [`eslint`](https://eslint.org/) with a custom `eslintrc` file, you may also need to add some extensions to recognize the Fastly types:

```json
{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ]
}
```

## Usage with TypeScript

[TypeScript](https://www.typescriptlang.org) extends JavaScript with syntax for types. You can use the Compute JavaScript SDK with TypeScript in two ways:

- **Built-in TypeScript support**: Works with source files that use only [_erasable_ syntax](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html#the---erasablesyntaxonly-option) (syntax that can be stripped out without affecting runtime behavior, such as type annotations). No transforms or type-checking are done.
- **Full TypeScript support**: Uses the `typescript` package (or another compiler) to pre-emit JavaScript from your TypeScript sources. This supports all TypeScript features, including enums, namespaces, and class parameter properties.

### Built-in TypeScript (lightweight mode)

The SDK can directly execute TypeScript files as long as they contain only erasable syntax. This is the easiest way to add type annotations to an existing JavaScript project.

> **HINT:** To start a new project with TypeScript configured this way, see the [TypeScript default starter kit](https://github.com/fastly/compute-starter-kit-typescript-default).

**Important:**

- No type checking is performed by default
- A `tsconfig.json` is optional but recommended for IDE support and to perform a type checking pass of your code at pre-build

> **HINT:** If you need TypeScript features that require transformations (for example: enums, namespaces, class parameter properties) or that depend on `tsconfig.json` settings (like path aliases or down-level transforms), use [Full TypeScript](https://www.fastly.com/documentation/guides/compute/developer-guides/javascript#full-typescript).

**Steps:**

1. **Rename your entry point file** to `.ts` and add type annotations:

   ```sh
   mv src/index.js src/index.ts
   ```

   For example, add the `FetchEvent` type to the `event` object in your handler.

   ```typescript
   async function handler(event: FetchEvent) {
     // ...
   }
   ```

2. **Update the `build` script** to reference the `.ts` file:

   ```json
   {
     "scripts": {
       "build": "js-compute-runtime src/index.ts bin/main.wasm"
     }
   }
   ```

3. **(Recommended) Add a `tsconfig.json` file** to the root of your project:

   > **HINT:** If parts of your source files will still be in JavaScript, also add `"allowJs": true` under `"compilerOptions"`.

   ```json
   {
     "compilerOptions": {
       "strict": true,
       "erasableSyntaxOnly": true,
       "skipLibCheck": true,
       "target": "ES2022",
       "lib": [ "ES2022" ],
       "module": "ESNext",
       "moduleResolution": "bundler",
       "customConditions": [ "fastly" ],
       "types": [ "@fastly/js-compute" ],
       "esModuleInterop": true,
       "isolatedModules": true,
       "verbatimModuleSyntax": true,
       "allowImportingTsExtensions": true
     }
   }
   ```

   Key options:

   - `erasableSyntaxOnly: true` - disallow TypeScript constructs that need transforms
   - `customConditions: ["fastly"]` - **required** for Fastly-specific types (conditional exports)
   - `types: ["@fastly/js-compute"]` - add globals provided by the runtime

   > **IMPORTANT:** Fastly-specific types (for `fastly:*` modules and runtime globals like `FetchEvent`) are published by `@fastly/js-compute` under the [`fastly` runtime key](https://runtime-keys.proposal.wintercg.org/#fastly). Your `tsconfig.json` must include this value in `customConditions`, or you may see missing or incorrect types.

   For more information about the properties that can be set in this file, see the [tsconfig.json documentation](https://www.typescriptlang.org/tsconfig/).

4. **(Recommended) Add pre-build type checking** by installing `typescript`:

   ```sh
   npm install --save-dev typescript
   ```

   And in `package.json`:

   ```json
   {
     "scripts": {
       "prebuild": "tsc --noEmit"
     }
   }
   ```

   > **NOTE:** Type checking requires the `tsconfig.json` configuration file from the previous step.

5. **Run or deploy** as usual.

### Full TypeScript

For full TypeScript support, compile your TypeScript to JavaScript before handing it to the SDK.

> **HINT:** To start a new project with TypeScript configured this way, see the [TypeScript full starter kit](https://github.com/fastly/compute-starter-kit-typescript-full).

**Steps:**

1. **Add a `tsconfig.json` file** to the root of your project:

   > **HINT:** If parts of your source files will still be in JavaScript, also add `"allowJs": true` under `"compilerOptions"`.

   ```json
   {
     "compilerOptions": {
       "strict": true,
       "skipLibCheck": true,
       "target": "ES2022",
       "lib": [ "ES2022" ],
       "module": "ESNext",
       "moduleResolution": "bundler",
       "customConditions": [ "fastly" ],
       "types": [ "@fastly/js-compute" ],
       "esModuleInterop": true,
       "isolatedModules": true,
       "rootDir": "src",
       "outDir": "build"
     },
     "include": [
       "./src/**/*"
     ],
     "exclude": [
       "node_modules"
     ]
   }
   ```

   Key options:

   - `customConditions: ["fastly"]` - **required** for Fastly-specific types (conditional exports)
   - `types: ["@fastly/js-compute"]` - add globals provided by the runtime

   > **IMPORTANT:** Fastly-specific types (for `fastly:*` modules and runtime globals like `FetchEvent`) are published by `@fastly/js-compute` under the [`fastly` runtime key](https://runtime-keys.proposal.wintercg.org/#fastly). Your `tsconfig.json` must include this value in `customConditions`, or you may see missing or incorrect types.

   For more information about the properties that can be set in this file, see the [tsconfig.json documentation](https://www.typescriptlang.org/tsconfig/).

2. **Rename your entry point file** to `.ts` and add type annotations:

   ```sh
   mv src/index.js src/index.ts
   ```

   For example, add the `FetchEvent` type to the `event` object in your handler.

   ```typescript
   async function handler(event: FetchEvent) {
     // ...
   }
   ```

3. **Install and configure TypeScript** to emit JavaScript before builds:

   ```sh
   npm install --save-dev typescript
   ```

   ```json
   {
     "scripts": {
       "prebuild": "tsc"
     }
   }
   ```

   This will emit JavaScript to `build/` (e.g. `src/index.ts` → `build/index.js`).

4. **Update your build script**:

   ```json
   {
     "scripts": {
       "build": "js-compute-runtime build/index.js bin/main.wasm"
     }
   }
   ```

5. **Run or deploy** as usual.

### Troubleshooting: Fastly types not resolving

If your project's TypeScript configuration does not enable the `fastly` condition, Fastly-specific modules and runtime globals may fail to type check, producing error messages such as the following.

```text
error TS2307: Cannot find module 'fastly:env' or its corresponding type declarations.
error TS2307: Cannot find module 'fastly:html-rewriter' or its corresponding type declarations.
error TS2304: Cannot find name 'addEventListener'.
error TS2304: Cannot find name 'FetchEvent'.
```

**Fix**

Add the Fastly condition to your `tsconfig.json`:

```json
{
  "compilerOptions": {
    "customConditions": ["fastly"]
  }
}
```

**Notes**

- Fastly starter kits using TypeScript already include this configuration (since September 2023 / JavaScript SDK v3.35).
- This issue primarily affects older projects or projects with custom TypeScript configurations.

## Module bundling

Compute applications written in JavaScript that have the [standard structure](https://www.fastly.com/documentation/guides/compute/developer-guides/javascript#project-layout) and [standard dependencies](https://www.fastly.com/documentation/guides/compute/developer-guides/javascript#using-dependencies) can be built using the tools included with the JavaScript SDK. However, if your application needs to perform advanced tasks at build time, such as replacing global modules, providing polyfills, or transforming code (such as JSX, TypeScript, or JavaScript syntax proposals), you can use a module bundler.

> **WARNING:** Using a module bundler may cause the resulting bundle - and therefore the compiled Wasm package - to become significantly larger. Compute packages are subject to [platform and account-level limits](https://www.fastly.com/documentation/guides/compute/getting-started-with-compute/#limitations-and-constraints) on the maximum package size.

Typically, the module bundler is added to a Compute application project as a dependency and run in a `prebuild` script, configured to place its output in an intermediate location. The `build` script is also modified to use those intermediate files as its input. Note that the specific configuration will vary depending on your application's needs and the bundler.

The Fastly Compute JavaScript SDK makes some of its features available through [`fastly:` namespaced imports](https://js-compute-reference-docs.edgecompute.app/docs/#understanding-the-javascript-sdk), such as [`fastly:env`](https://js-compute-reference-docs.edgecompute.app/docs/fastly:env/env). In order to use these imports with a bundler, you may need to add configuration so that it recognizes these as platform imports rather than modules that exist in `node_modules`.

The following are some bundlers that have been seen to work with Fastly Compute along with some recommended configuration options.

### Using webpack

When using [webpack](https://webpack.js.org/) as a bundler with Fastly Compute, use the following recommended values in its `webpack.config.js` configuration file at minimum:

- `target` - Set to `false`, as the [predefined targets provided by webpack do not match](https://webpack.js.org/configuration/target/#false) the Compute platform.
- `devtool` - Set to `false`, as incremental builds do not make sense in the context of Compute.
- `output.filename` - The name of the intermediate file. Use a file with the `.cjs` extension.
- `output.chunkFormat` - Set to `'commonjs'`.
- `output.library.type` - Set to `'commonjs'`.
  - The output format supported by webpack is the older [CommonJS module](https://nodejs.org/api/modules.html) (ES module output by webpack is [still experimental](https://webpack.js.org/configuration/experiments/#experimentsoutputmodule) and not fully supported). During the bundling process, code is transformed to CommonJS syntax (e.g., `import` and `export` are converted to `require()` and `module.exports =`). Create the bundled output file with a `.cjs` extension, so that the file is treated as a CommonJS module regardless of the `"type"` value of the package.
- `externals` - Set to `[/^fastly:.*$/]`. Indicates to webpack that `fastly:` imports should be looked up as platform imports rather than modules under `node_modules`.
- `resolve.extensions` - Set to `[]`.
- `resolve.conditionNames` - Set to `['fastly', '...']`. Indicates to webpack to load the appropriate variant of packages that offer packages that recognize the `fastly` platform. The value `'...'` indicates to webpack to insert the default list of conditions here.
  - If you're using `react-dom`, use `['fastly', 'edge-light', '...']`.

Using these as a starting point, you can further customize the configuration to meet your needs. For example:

- The [`module` section](https://webpack.js.org/configuration/module/) can be used to determine how [different types of modules](https://webpack.js.org/concepts/modules) will be treated.
- The [`plugins` section](https://webpack.js.org/configuration/plugins/) or [`resolve` section](https://webpack.js.org/configuration/resolve/) are useful for [shimming](https://webpack.js.org/guides/shimming/) and [redirecting module resolution](https://webpack.js.org/configuration/resolve/#resolvefallback) when your code relies on Node.js built-ins, proposals, or newer standards.
- Refer to the [webpack configuration documentation](https://webpack.js.org/configuration/) for more details.

> **HINT:** A great way to get up and running with webpack for bundling a Compute application is by using the [Compute starter kit for webpack](https://github.com/fastly/compute-starter-kit-javascript-webpack), which comes complete with a starting point `webpack.config.js` file.

### Using esbuild

When using [esbuild](https://esbuild.github.io) as a bundler with Fastly Compute, it's recommended to use the JavaScript API to run esbuild. Use the following recommended configuration values at minimum:

- `bundle` - Set to `true`. Tells esbuild to run in bundle mode, in other words, to inline the dependency modules into the output file.
- `platform` - Set to `'neutral'` - Tells esbuild to use [neutral defaults](https://esbuild.github.io/api/#platform).
- `external` - Set to `['fastly:*']`. Indicates to esbuild that `fastly:` imports should be looked up as platform imports rather than modules under `node_modules`.
- `conditions` - Set to `['fastly']`.
  - If you're using `react-dom`, use `['fastly', 'edge-light']`.

Using these as a starting point, you can further customize the configuration to meet your needs. For example:

- [`loader`](https://esbuild.github.io/api/#loader) - can be used to determine how input files will be treated.
- [`alias`](https://esbuild.github.io/api/#alias) - can be used to replace modules at build time.
- [`plugins`](https://esbuild.github.io/plugins/) - can be used to include [plugins](https://esbuild.github.io/plugins/), which are modules that can inject code at various points in time during bundling.
- Refer to the [esbuild Build options](https://esbuild.github.io/api/#build) for more details.

> **HINT:** A great way to get up and running with esbuild for bundling a Compute application is by using the [Compute starter kit for esbuild](https://github.com/fastly/compute-starter-kit-javascript-esbuild), which comes complete with a starting point configuration file.

## Testing and debugging

Logging is the main mechanism to debug Compute programs. Log output from live services can be monitored via live [log tailing](https://www.fastly.com/documentation/guides/compute/developer-guides/testing/#live-log-monitoring-in-your-console). The [local test server](https://www.fastly.com/documentation/guides/compute/developer-guides/testing/#running-a-local-testing-server) and [Fastly Fiddle](https://fiddle.fastly.dev) display log output automatically. See [Testing & debugging](https://www.fastly.com/documentation/guides/compute/developer-guides/testing) for more information about choosing an environment in which to test your program.

Most common logging requirements involve HTTP requests and responses. It's important to do this in a way that doesn't affect the main program logic, since consuming a response body can only be done once. The following example demonstrates a `console.log` statement for request headers, response headers, request body and response body:

[Try it in Fastly Fiddle](https://fiddle.fastly.dev/6d1097a9)

Since the bodies of HTTP requests and responses in Compute programs are streams, we are consuming the stream using the convenience `.text()` method and then logging the resulting data. In JavaScript once the `.body` property of a request or response has been read it cannot be used by `fetch` or `respondWith`, so we use the extracted body data to construct a new `Request` or `Response` after logging the body.

> **WARNING:** Logging body streams in this way will likely slow down your program, and may trigger a memory limit if the payload is large.

### Unit testing

You may choose to write unit tests for small independent pieces of your JavaScript code intended for the Compute platform. However, Compute programs 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.

> **HINT:** You can use [`@fastly/compute-testing`](https://www.npmjs.com/package/@fastly/compute-testing) to write tests from Node.js, against a local or remote Fastly Compute application.
