@fastly/compute-js-static-publish is a static publisher for Fastly Compute. Got a folder full of static files? Point the tool at it, and you get a Compute service that serves them right from the edge, with no origin server needed.
Back in 2022, I wrote about the early versions of this tool, when it was at 2.0.0. Well, a lot has happened since then! We're now at version 8, and the biggest change is under the hood: your files no longer have to live inside your application. Let's take a look at what's new, especially if you haven't checked in recently.
Getting Started
Let's say you have the files for a website in a directory called public. Type the following to scaffold a Compute application (you’ll need to have Node.js 20.11 or newer installed):
This generates a Compute application in a directory called compute-js. Before you do anything else, you can try it out in Fastly's local development server. You’ll need the Fastly CLI, but no Fastly account or service is needed yet:
Browse to http://127.0.0.1:7676 and there's your website, served from a simulated KV Store.
Happy with how it looks? To go live, you’ll need a Fastly account and for your Fastly API token to be set in the FASTLY_API_TOKEN environment variable. Run these commands:
The first command deploys the app and creates the KV Store, and the second uploads your files. And that's it! You now have a website running at the edge, and you haven't written a single line of code.
Where Did All the Static Files Go?
If you read the 2022 post, you may remember the answer to this question: everything got compiled into a single Wasm binary that was deployed to Fastly. That's simple, but it meant your whole site had to fit within the package size limit, and every content change meant a new build and deploy. Version 4 added another option: storing the files in Fastly's KV Store and reading them at request time. For a while, you could pick either approach. Then in version 7, we dropped the inline Wasm option altogether, so KV Store is how it works now.
Here's what that gets you. Files are stored under a key based on the hash of their contents, so identical files are only stored once, even across publishes. Files over 20 MB are split into chunks behind the scenes and put back together when they're read. And since your content isn't baked into the app, the 50 MB package limit (since increased to 100 MB) I warned you about in the first post no longer limits the size of your site, and publishing new content doesn't require deploying a new Wasm binary. You only need to redeploy the app when you change your app's code or its configuration.
Version 8 adds one more place your files can live: Fastly Object Storage (or any S3-compatible storage). Because it's S3-compatible, you're free to bring your own storage, and your files stay in one piece instead of being split the way KV Store splits files over 20 MB. Storage costs also tend to be lower with Fastly Object Storage (see the pricing page for current rates). This option is in beta, and KV Store is still the default, so you don't need to change anything. If you'd like to give Object Storage a try, the project GitHub repository has the details on configuring your credentials.
Collections: Publish, Preview, Promote
Here's where having your content separate from your app really pays off: you can have more than one version of your site published at the same time. We call each version a collection, which is a named set of files along with the server settings that go with them. You might have live, staging, and preview-123 all side by side.
Let's say you want to publish a staging version that cleans up after itself in a week:
And once you're happy with how it looks, promote it to live:
Expired collections stop being served, and the clean command removes any files that no collection uses anymore. To actually view a collection, your app picks the active collection on each request, and the tool comes with helpers to do that based on the hostname (like preview-pr-123.example.com), a cookie, a header, a URL path, or a value in a Fastly Config Store. Imagine giving every pull request its own preview URL!
Faster and Leaner
Compression happens when you publish, not when someone visits. By default, text-based files are stored in Brotli and gzip variants alongside the original, and the server picks the smallest one that the visitor's browser accepts. The server also handles conditional requests, answering with 304 Not Modified when the visitor already has the latest copy.
Version 8 also makes publishing itself a lot quicker. The tool now greatly shortens the amount of time required to check what's already in storage, skips files that haven't changed, scans and compresses files in parallel, and uploads to storage (KV Store or Object Storage) in batches. If you'd rather trade slightly larger files for faster compression, you can lower the Brotli quality in your publish config.
There's also a new, optional response cache for your running app. Turn it on with setResponseCache(), and whole responses are cached using Compute's Core Cache API, so repeat requests skip reading from storage entirely. That also means that once a response is cached, any difference in latency between storage backends only affects the first request. Responses larger than 8 MiB by default are served straight from storage, though you can raise that limit with maxBodySize. Publishing new content purges the cache for that collection automatically. And if you're curious where the time is going, setServerTimingRequestHeader() adds a Server-Timing header to requests that ask for it, including whether the cache hit or missed. Both are off by default.
And Now, the JavaScript Part
So far, everything I've described works out of the box, with no coding at all, and you may be thinking that the tool may as well have been called Compute Static Publish. But remember it's called Compute JS Static Publish. That's because the project that gets generated is a regular Fastly Compute application written in JavaScript, and that's where this tool gets really interesting. If all you need is to serve static files, you never have to touch it. But when you do want to add something, it's your code, so go for it.
At the center of the generated app is the PublisherServer class, which maps incoming requests to your published files and takes care of index files, 404 pages, single-page-app fallbacks, compression, and caching headers. If you remember reaching into an assets object for a file's contents in the old days with this library, that's gone; PublisherServer is how you work with your content now.
When PublisherServer can't handle a request, it returns null, which lets you wrap your own logic around it. For example, here's a static site that also has a small API endpoint:
From here you can extend the app further to add authentication, redirects, A/B testing, or collection selection. And if you need access to your static files programmatically, getMatchingAsset() gives you access to a file's bytes or its metadata, such as its content type, size, and hash, so you can serve it yourself or read its contents in your own code.
And More!
There's more in the box than fits in one post! Here are a few highlights, with the details over in the :
- A full command-line toolkit. The single command from 2022 has grown into git-style subcommands: publish-content, clean, and collections (with list, promote, delete, and update-expiration). Anything that changes things supports --dry-run, so you can see what would happen first.
A full command-line toolkit. The single command from 2022 has grown into git-style subcommands: publish-content, clean, and collections (with list, promote, delete, and update-expiration). Anything that changes things supports --dry-run, so you can see what would happen first.
- Automatic purging. After publish-content and collections delete, version 8 soft-purges the affected collection's surrogate key, so stale cached responses don't hang around. The --fastly-service-id and --purge-environment options let you control what gets purged.
Automatic purging. After publish-content and collections delete, version 8 soft-purges the affected collection's surrogate key, so stale cached responses don't hang around. The --fastly-service-id and --purge-environment options let you control what gets purged.
- Local development. In KV Store mode, the --local flag points the management commands at a simulated KV Store, so you can try out the whole publish workflow without a Fastly service. In Object Storage (S3) mode there's no local simulation, but you can still develop locally: publish to your bucket, and the local development server reads your content directly from it. Use a separate bucket or collection for development, so that you don't change what production serves.
Local development. In KV Store mode, the --local flag points the management commands at a simulated KV Store, so you can try out the whole publish workflow without a Fastly service. In Object Storage (S3) mode there's no local simulation, but you can still develop locally: publish to your bucket, and the local development server reads your content directly from it. Use a separate bucket or collection for development, so that you don't change what production serves.
- Fine-grained control. Include or exclude files, add or override content types, choose which compression variants to generate, and set per-collection server settings like SPA fallbacks and custom 404 pages.
Fine-grained control. Include or exclude files, add or override content types, choose which compression variants to generate, and set per-collection server settings like SPA fallbacks and custom 404 pages.
Upgrading
If you're coming from version 6 or earlier, plan on re-scaffolding your app, because version 7 was a major rewrite. It dropped inlining files into the Wasm binary, along with support for module assets. If you rely on module assets, the v6 branch is still there for you.
If you're on version 7 and using KV Store, good news: you don't need to re-scaffold. Install version 8 in your compute-js directory, make sure the tool can find a Fastly API token (set FASTLY_API_TOKEN or pass --fastly-api-token, since it no longer falls back to your Fastly CLI login), and then rebuild and deploy your app. Version 8 will keep serving the content you've already published. The migration guide covers the other changes to watch for.
Ready to Give it a Try?
@fastly/compute-js-static-publish is a Fastly Labs project, and we'd love to hear what you build with it! Version 8 is ready for use on npm, so head over to the GitHub repository for the full documentation to try it out, and file an issue there if you run into anything unexpected.









