Building a PWA with Netlify Functions

A couple of weeks ago I took Netlify for a spin. I found the platform to be much easier to use than I’d anticipated. I was keen to test more features.

Netlify Functions provide back-end functionality through API endpoints. They’re powered by AWS Lambda. I’m using AWS for my contact form. It took me forever to get working. If you asked me today I legitimately could not tell you how. An abstracted layer sounds like a good feature to me.

The Mission

In the spirit of a “friday coding” hackathon I set myself the mission: in one day build a progressive web app that uses Netlify functions. Since I had the domain lying around — snapped it up during the gold rush — I worked backwards from there.

in which the only standards are those that’re out the door by beer o’clock

Inspiration: I’ve been having a problem with short URLs recently. They’ll often redirect through tracking domains that are purposefully blocked by either my browser or router. Seems like a decent task for a remote function?

Eavesdrop works as follows:

  1. User inputs a short URL
  2. Form is posted to a Netlify function
  3. Function runs in the AWS cloud
  4. Unshortened URL is returned

It’s far from an original idea but it’s got a cool domain.

I coded up the front-end UI on CodePen. I then glued it all together with React. You can find the source code on GitHub. Unfortunately I spent far too long animating the logo so the visual UI feedback is lacking. Functionally it works great though which is the point. I had to remind myself that (after messing with CSS for four hours).

On the front-end the magic happens with a fetch:

fetch('/.netlify/functions/unshorten', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  body: JSON.stringify({url})
  .then(response => response.json())
  .then(data => {
    /* Success! do stuff... */
  .catch(err => {
    /* Error! do stuff... */
  .finally(() => {
    /* clean up? */

On the back-end the real magic happens. The unshorten function I borrowed heavily from tall by Luciano Mammino. I adapted and wrapped it in Netlify’s handler:

module.exports = {
  handler: (event, context, callback) => {
    const body = JSON.parse(event.body);
      .then(url => {
        callback(null, {
          statusCode: 200,
          body: JSON.stringify({url})

It’s a Node script that follows 301 redirects until someone gives up. It’s not perfect but I’m not prepared to read the HTTP spec to fix edge cases. This project is a proof-of-concept after all.

It took me a little while to get the function working. My fault; bad API requests and data formatting. Testing was excruciatingly slow until I discovered the Netlify CLI allows for local development and function invoking.

Then it worked! Nice. Mission complete.

Eavesdrop could use some polish but for a day’s work I’m happy with the results. Particularly with how simple it was to deploy on Netlify. Sure, I could roll my own solution on AWS like I did with my contact form, but I’d rather pay for simplicity and convenience. And since I pay nothing on the free plan — even better!

No doubt I’ll be testing other JAMstack services so watch this space.