I’ve been using Netlify as my host for a while now and have consistently loved the experience. In my time with the service, I haven’t been utilizing it much more than a static site host.
I really want to use more of their features and make my static site more dynamic.
Demo: On this site
GitHub Repository: repo
The Challenge: Share data from iOS Shortcuts to my Netlify site
When iOS released Shortcuts with iOS12, I saw Netlify’s Twitter account share a video of someone deploying their site with a shortcut. I thought that was excellent, so I decided to go that route.
With this challenge, I wanted to learn more about Serverless functions and Netlify’s API. I did some reading and based my solution off of Phil Hawkworth’s excellent JAMstack comment engine functionality.
Practical CSS Grid 50% off!
Whether you're new to CSS Grid or have played with it, finding practical examples of this new layout mechanism is the best way to learn it's power. Sign up below to learn more about my Practical CSS Grid course and get 50% off when it comes out!Sign Up Now
- Works with my Static Site Generator: Jekyll
- Does more than build my site
- Be able to be built over the course of a hackathon (I built the basics of this over the course of HACKmemphis, an annual hackathon I organize)
I decided on a small set of features. Take a short dictation and photo from shortcuts and post it as a status to my site.
Here was the flow I built from:
- Run the Shortcut which will take a photo and ask me “What’s happening?”
- Submit the string and image URI to a serverless function in Netlify
- Have the function process the data and submit to a Netlify form (which gets used as a data store)
- Generate a build/deploy
- On build, have Gulp make an API call to Netlify to get all the form data and create a JSON object that Jekyll can ingest
- Stretch Goal Download the image from 3rd party (imgur) to be local to my project
Setting up the shortcut
The iOS Shortcuts app allows you to automate many pieces of your workflow. It has access to multiple Apple apps as well as some third party apps.
Here’s the shortcut I’ve created:
- Use Apple’s Camera to take a photo
- Pass that photo to the Imgur app to upload to Imgur (need a place online for these to go; this was the easiest)
- Have Siri ask me “What’s happening” (more for demo purposes at HACKmemphis than anything else)
- Use the Dictation skill to record and type my response
- Pass that string plus the image URL from imgur to a Netlify function URL as parameters
- Hit that URL with Safari
Creating a Netlify function to handle the data
Now that we’re sending the data to a URL on our site, we need something to handle that data. Remember, this is a static site.
To do this, we can use Netlify’s awesome Functions. It’s important to note that this DOES cost money past a certain point. This sort of function would take a LOT of use to get past the free usage tier, though.
Our function is split into two parts: formatting data and posting data to a database.
Step 1: Ingest the data from URL parameters and sanitize it for use
In this area, I discovered my first issue. The URLs I was getting from Imgur’s iOS shortcut came in different formats. Both of the formats were not the pure image. I never found the root cause of the varying formats, but baked in protections for that. To get the direct image URL, all it took was rewriting the URL string with
download in it.
Step 2: Post that data to a form on Netlify
First, note that we’ll be using environment variables for our form endpoint as well as our form name. This removes “sensitive” data from our git repository. If someone has a form name, they can post to your forms and cost you money.
“Gotcha” No. 1: Simulating environment variables locally. There’s a lovely package on NPM called dotenv that I was able to set up to handle this. Create a
process.env.VARIABLE_NAME by running
“Gotcha” No. 2: How do you post to a Netlify form? As it turns out, you can post to any URL on your site as long as you have the proper form name.
The rest of what’s happening in this code is a POST request using the Node “request” module to send our data to our form.
“Gotcha No. 3: In order for Netlify to understand our POST, our site has to have an HTML form with this name. Netlify uses the HTML in your repository to figure out what processing your site needs.
The site can handle this with a single route for all its forms. In this repository, I’m using a form-stub.html which isn’t linked anywhere.
In this example, we’re using Netlify’s form system as a database. You could create a third-party database with Firebase or a similar tool. In my case, I wanted to use as few third-parties as possible.
Using the data in our site
Most static site generators have some way of dealing with data files. In Jekyll, if you put JSON files in a
_data directory, you gain access to those variables in your templates.
We’ll be requesting this data during the build process, so we need to utilize a task runner for this. My current tool of choice for tasks is GulpJS.
In this example, we use two Gulp tasks: one for building our data and one for downloading the images.
Getting the data from Netlify
Before we can use the data, we’ve got to fetch it. Netlify provides a handy API for dealing with their forms. There’s a lovely GET method that will return the form submissions. This allows us to use the form functionality as a database for these updates.
Using this data, we’ll create a JSON file in
_data to store our information.
To lower complexity for this example, we’ll clean the previous file first. To do this, we’ll use
fs.truncate to truncate the file to 0 characters.
Once we have our file cleaned, we need to create the new data from the API request’s body. We use Array.map() to create a the object for each status as expected by our Jekyll templates.
Once we have the data how we want it, we’ll use fs.writeFileSync() to write this array to our data file.
Note: I’m utilizing synchronous versions of
fs module functions. This made the process a bit easier and clearer.
I’m also using environment variables again to protect the form ID as well as my API key. Remember to always keep your sensitive data in these variables and not in your repo.
The finished Gulp task looks like this:
Bonus: Download the images to the project via Imgur
Now that we have the data, we can write a function to fetch the images and store them locally. This will cut down on reliance of a third-party website to serve static content.
First, we need to determine all the images we need from the data. To do that, we’ll read the statuses.json file and grab the image IDs from the data.
From there, we could just download each image from Imgur to our project. When we have a lot of statuses, that could end up taking a long time.
To help mitigate that, I let this process run locally when I’m working on my site and then push the images to GitHub.
Then, I can compare the image IDs I need to what files already exist and only download the latest.
Writing a Jekyll template to use the data
Jekyll has access to JSON data built directly into its templates. By utilizing
site.data.<filename>, we gain access to the data either as an array to loop through or an object to pull data from.
In our case, we’ll loop through the data as an array using
for status in site.data.statuses.
Setting up the build and serve functions
Once we have everything configured the way we want, we need tell Gulp the order to run our commands. In this case, we want a development serving command and a production
The main differences in these commands are serving vs building Jekyll and adding Netlify’s lambda build command to the build functionality.
lambda:build function runs a shell command for the Netlify-lambda-cli package.
netlify-lambda build lambda_build
In this instance
lambda_build is the name of the directory in which our lambda function’s source exists. The source then gets published to the
functions directory which Netlify is expecting.
For local testing, you can run
netlify-lambda server lambda_build and it will run a server for you.
Where to go from here?
I believe the future is in static architecture. This is a small snapshot at building that future.
There’s a lot of things in this project that felt a little wonky. They felt that way because my brain is used to thinking of things in terms of server code. Once I got past that, I felt POWERFUL.
This seems ridiculous in some ways, but there’s immense power to be hand by offloading your server to someone else. There’s immense performance by handling all pages as static HTML.
It’s an interesting time to be doing this work. What would you build in this workflow?