AWS Lambda functions via Netlify
This is note 4 of 5 in a series on publishing automation.
If you're a frontend developer in 2020, the serverless movement has given you agency that you've never had before: writing server-side code without all the nuance and pain of infrastructure configuration. Netlify, a platform for hosting and continuously deploying your site without touching tools like Jenkins or Bamboo, takes the serverless idea and pushes the bill even further with Netlify Functions. Now, creating a call-able REST API that runs an AWS Lambda function is as simple as creating a directory in our repo, flipping a switch in Netlify and writing the code.
To achieve the dream of using Bear as an authoring tool for the web, I decided to leverage the power of Netlify functions to write a service callable via Scriptable in iOS. The complete flow shapes up like this:
- Write a note in Bear
- Click 'Share' from the note
- Click 'Run Script' in Scriptable to run some JavaScript
- The script makes a PUT request to our Netlify function, sending the note's content
- Our function parses the note content and makes requests to Contentful, sending the note there, creating and publishing content blocks
- Contentful triggers Netlify to re-deploy the website when the new note is published
- Netlify deploys the site and the note is viewable from the website
Define the scope of our function
Before hacking away, it's helpful to define exactly what the function should achieve:
- Define a REST endpoint with a PUT method
- Expose the resource on this domain, https://tyhopp.com
- Require a special header, in our case
bear-to-contentful
, with a secret access key so we have permission to create objects in Contentful - Parse the raw markdown note payload and format into a structured object that we can send to the Contentful Content Management API
- Call the Contentful API to create the note in Contentful
- Call the Contentful API to publish the note in Contentful
- Return a success or error response to the caller, in our case Scriptable
With this spec, let's dive deeper into the code required to pull it off.
But first, some setup
The whole point of using Netlify over the cloud platforms like AWS, Azure and GCP directly is to avoid all the required steps before you can actually write your code: setting up a billing account, provisioning permissions, configuring API gateways, etc. The luxury of Netlify Functions is we don't even need an account, and the same generous free tier you get on all the platforms (~125k requests and 100 hours of run time) applies here too. Still, you do have to do a couple steps of setup:
- In your Netlify web interface, go to Settings > Functions and enter the path to your functions directory in your site, in my case
./functions
. - Install netlify-dev, the tool they created to enable us to test our functions locally before deploying. In any sane workflow, this is a necessary step. Follow the docs to get up and running.
Write the code
To see the finished function currently in production, check it out on GitHub - I open sourced this site recently! 🍻
Now that we have our path declared and our workflow setup, we can commence with the actual programming. A very useful set of examples can be found on this playground site, progressing from hello world to writing a Slack integration. Copy and paste some code to get a feel for it.
For our case, the root function looks like this:
;
;
/**
* A lambda function to create/update and publish blog posts in Contentful.
*/
exports.handler = ;
We export a single nameless, asynchronous function and pass in the event
parameter, which contains all the properties we need to parse the request: headers
, body
and httpMethod
. In this function, we only need two checks:
- Make sure the
bear-to-contentful
header exists and the value matches our environment variable defined in Netlify (which we got from Contentful, to be covered in greater detail in the next note). - Make sure the
httpMethod
isPOST
orPUT
, since we only want to focus on creating and updating blog posts for now. From Scriptable we will call PUT, becausePUT
works in both create and update cases whilePOST
works only for creation.
If both conditions are met, we send the body into our createBlogPost
function and try to parse, create and publish to Contentful. The key is that if at any point in the function we want to throw an error or return a successful response, we do that by returning an object with a statusCode
and body
.
Define an interface
Before diving into the createBlogPost
function, we have to consider how our script will parse the things that our Contentful blog post content model requires (more on content modeling in the next post). Even if you're not using Contentful for your cases, your CMS will still need to differentiate between things like your title
, description
and body
, making this step generally relevant. In my case, I need to parse out these pieces:
- title
- slug
- date
- short description
- category
- body
With that in mind, I created a standard template that all blog posts I write in Bear will use. In doing this, we can create a predictable request body that is more easily parsable in our createBlogPost
function. Here's what it looks like in markdown:
Parse the note
In our cloud function, the parse and format utility functions do the regex parsing to break down the markdown above into an object:
/**
* Parses the input markdown and returns as an object.
* @param {string} text the raw markdown to process
*/
/**
* Formats a flat object into desired Contentful payload.
* @param {string} text the raw markdown to process
*/
;
module.exports =
There are dozens of ways we could write the regex, and I'm sure what's written above isn't the most efficient, but it works for our purposes. Once the parsing is done, we can move on to the tricky business of forming the fetch requests to the Contentful Content Management APIs, which will be the subject of the last article in this series:
Previous articles in this series:
Thanks for reading! Go home for more notes.