How We Built a Serverless Progressive Web App with Cloudflare Workers

A few days ago, we launched our new Progressive Web App ( Budgetimize is a personal finance application to gain valuable insights into your finances. We did this without setting up any servers. We didn’t have to worry about regions or uptime. Instead, we could focus on delivering the best experience to our users. In this article, we’ll explain how it works and why we love it.

Let’s start deploying our static website

Our Progressive Web App runs entirely in the browser on the client-side. We don’t use techniques like server-side-rendering or templating engines. All users receive the same static code.

Cloudflare Workers allows you to use a storage bucket (e.g. Amazon S3) to fetch static files and serve them to your users. You can use features like the Cloudflare edge cache to make future requests go even faster. While this doesn’t require us to manage any servers. We still need two services (Cloudflare Workers and Amazon S3) to deploy our website. And each time we update our website, we need to purge the Cloudflare cache and let our workers fetch the files again from S3.

Could we drop Amazon S3 and serve our static website entirely from Cloudflare? Turns out this is pretty easy for text files. You can inline them as a big string inside your worker-script. The only thing your worker needs to do is generate a response with that string and the correct headers. It’s blazingly fast since it only needs to return a simple string from a Cloudflare data center close to the user.

So far so good. A simple built-script takes our PWA-files, minifies it and inlines the code inside our worker script. But… our PWA doesn’t only contains text files, we also have icons, images and custom fonts. Bummer, those are binary files which we cannot inline in JavaScript. One solution would be to use some text-based encoding (like base64) to re-encode the binary files to a textual format. And then, at runtime, we could re-encode them to a binary format before serving them to our clients. Sad thing is that this blows up the size of our worker script (limited to 1 megabyte). And encoding them as text doesn’t feel quite right.

Let’s introduce WebAssembly

Cloudflare Workers also support loading WebAssembly files. For those who have never heard of WebAssembly. WebAssembly is a new low-level language for the web, close to other assembly languages. It can be much faster than JavaScript, and you can compile several other languages to it (e.g. Go or Rust).

And guess what, WebAssembly files are binary files. We can put whatever data we want in there, even our images and fonts, without resorting to some inefficient textual representation of it. The only thing we need is a simple WebAssembly program, which contains our binary resources as global variables. We can then access them through an ArrayBuffer in JavaScript and send them immediately to the client. Without having to mess with encodings. We could have used Go or Rust, but since the rest of our PWA is written in TypeScript, we choose for AssemblyScript. AssemblyScript is a subset of TypeScript, which compiles to WebAssembly.

Great, so now we have everything ready to deploy our entire code on Cloudflare. Inline the text-based resources (HTML, JavaScript) into the worker-script itself. Put the binary resources as a literal byte-array in AssemblyScript. Compile this to WebAssembly and get a small binary file. Now you only need to deploy both files to Cloudflare, which is a single API-call, and your code is released in data centers all over the world.

Performance all the way

We didn’t stop there. If you ever built a website, you know you should compress the resources to save your users’ bandwidth and make your website load fast. We compressed all our text-files with Brotli (a more efficient compression than gzip) and received… binary files. We cannot inline those in our worker-script. But as you might have guessed, we can put them in our WebAssembly file.

Final Result

This is the final result. We have one WebAssembly file which contains all our static resources. Compressed with the highest Brotli-level. Our worker-script loads these resources from WebAssembly and serves them directly to our users. Which means we get very fast response times. Cloudflare will use a worker close to the user. This worker only needs to send the requested resource (which is already in memory) back to the user. We never had to touch any servers, just plain and simple JavaScript.