Self host Polyfill.io

Published: January 15, 2018  •  javascript

In my previous blog post about Parcel I briefly mentioned polyfills and how to import them with Babel and TypeScript and I also demonstrated an alternative way with Polyfill.io.

When you develop a web application and want to support older browsers but also want to use the latest browser features you need a transpiler that rewrites the code into ES5 code and you need to include polyfills. One common approach with polyfills is to bundle them together with the application. The problem is that this increases the bundle size and users with modern browsers have to download code that their browser does not need.

An alternative is the Polyfill.io service. It's a service developed and maintained by the Financial Times and it works very simple. You send a request to the service and get back the requested polyfills, but only if your browser does not support the features natively.

In the simplest case you add this script tag into your site

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

This returns the default set of polyfills. See the documentation to see what is contained in this set

When you open the URL in a modern browser you get an almost empty file back and when you open it in a browser like IE11 you see polyfill code. Polyfill.io analyzes the User-Agent HTTP request header to determine what code it has to send back.

Instead of requesting a standard set of features it is better to request only the features that your application depends on. You can specify the features with the query parameter features and a comma separated string.

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise,fetch"></script>

The Polyfill.io website lists all available features. The string you need is the name of the feature in the leftmost column.


One issue with Polyfill.io is that your application depends on a third party service you don't have any control over. This is maybe not a big problem when the application already depends on a lot of other external services.

But when Polyfill.io is the only external dependency this might be an issue. Or you develop an in house web application, where you have to support older browsers, and the employees only have limited or no access to the Internet.

Fortunately this issue can be solved by installing Polyfill.io on your own server. The source code is public available on GitHub and it's written in JavaScript and Node.js. It should run on any platform where a Node.js runtime is available.


Installing

Here is a description on how you can install Polyfill.io on a Ubuntu Linux server. I tested this on version 17.10.

Update all packages

sudo apt update
sudo apt dist-upgrade

Install necessary tools for the build process and Node.js

sudo apt-get install unzip make gcc g++ python

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs

Download the polyfill.io source code and build it

Check the release page on GitHub to get the latest version number. At the time of writing this post (January 2018) it is v3.25.1

cd ~
mkdir polyfillbuild
cd polyfillbuild
wget https://github.com/Financial-Times/polyfill-service/archive/v3.25.1.zip
unzip v3.25.1.zip
rm v3.25.1.zip
cd polyfill-service-3.25.1
npm install --production
npm run build

At this stage of the installation you can test the service if it works.

bin/polyfill-service

Open a browser and point it to the ip address of the server and port 3000. If there is firewall installed on your server open port 3000. If you see the Polyfill.io website everything is okay. You can now stop the service with CTRL+c


User

Next we move the build directory to /opt/polyfill-service, create an unprivileged user that will run the service (polyfill) and change the ownership of the /opt/polyfill-service tree to the new polyfill user.

cd ..
sudo mv polyfill-service-3.25.1 /opt/polyfill-service
sudo useradd -r -s /bin/false --home /opt/polyfill-service polyfill
sudo chown polyfill:polyfill /opt/polyfill-service -R

systemd

Next step is to configure systemd so that the service automatically starts when the server boots up

sudo nano /lib/systemd/system/polyfill.service

Paste the following code into the editor. If port 3000 is already assigned to another service change it here (PORT=xxxxx)

[Unit]
Description=Polyfill Service
Documentation=https://polyfill.io/v2/docs/
After=network.target

[Service]
Type=simple
User=polyfill
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/opt/polyfill-service/bin/polyfill-service
WorkingDirectory=/opt/polyfill-service
Restart=on-failure

[Install]
WantedBy=multi-user.target

Save and close the editor

Reload systemd. Call this command every time you change /lib/systemd/system/polyfill.service

sudo systemctl daemon-reload

Now start the Polyfill.io service with systemd and check the status.

sudo systemctl start polyfill
sudo systemctl status polyfill

When systemd was able to start the service you should see active (running) in the status output

If you want to stop or restart the service you call systemctl with these options.

sudo systemctl stop polyfill
sudo systemctl restart polyfill

The service will not yet automatically start the next time the computer boots up. You have to enable it first.

sudo systemctl enable polyfill

You can check if a service is enabled with

sudo systemctl is-enabled polyfill

And if it's running with

sudo systemctl is-active polyfill

If you no longer want to start the service at boot time you can disable it. This does not stop the service it only removes it from the auto startup configuration.

sudo systemctl disable polyfill

And if you need to check the log file you can use this command

sudo journalctl -u polyfill

Proxy

If Polyfill.io is the only service running on this server you could use the setup like this, maybe change the port to 80 but then you need to run the service as root.

When you have many services running on one server you often have a reverse proxy installed. Most of the time I use nginx for this. A simple setup only need a proxy_pass directive

location / {
     proxy_pass http://127.0.0.1:3000/;
}

Docker

When you are more interested in a Docker setup I can point you to this project:
https://github.com/radeno/polyfill-service-docker
In the etc/nginx folder you also find a more advanced nginx configuraton with caching.


Polyfill.io with Babel

In this last section I want to show you an example with a Parcel / Babel project and Polyfill.io (or your own, newly installed Polyfill.io server)

The application uses async/await, const, of loop and the Fetch API. All features that will not run on an older ES5 browser like IE11.

import 'regenerator-runtime/runtime';

(async () => {
  const response = await 
                     fetch('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson');
  const geojson = await response.json();
			
  for (const feature of geojson.features) {
    console.log(feature.properties.mag);
  }
})();

Babel transpiles the language features like async/await into ES5 code. The code that Babel generates for async/await depends on a module called regenerator-runtime that we have to import into the code (npm install regenerator-runtime). This is not a polyfill, just a common module to keep the generated more concise.

The transpiled code also depends on Promise and Symbol (for the of loop). These are all features that can be polyfilled. And the Fetch API is also not available on IE11 but can be polyfilled too.

The Polyfill.io URL for this simple application looks like this

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise,fetch,Array.prototype.@@iterator">
</script>

When you build the project (npm run build) you see that the bundle is only 8KB and it works when you open it with IE11. Users on a modern browser don't have to download any polyfill. Although all users now have to make an additional request to Polyfill.io or your own server.

You find the source code for this project on GitHub: https://github.com/ralscha/blog/tree/master/parcel/polyfill-io