Blog post filter with Netlify Functions
I'm so excited. Last week, I wrote and deployed my very first serverless functionality: filtering my blog posts by category. You can test it out at /blog main page.
The Idea
I have quite a few blog posts on this website. On the current list, there's way over 100 posts and I wanted to add a way to filter them by category since the variety of posts in my blog is quite a lot and not everyone is interested in everything.
My current stack is this: blog posts are either in local markdown files or written in headless Ghost CMS system, the website is built with Eleventy static site generator and it's all hosted in Netlify.
I wanted to maintain the simplicity of static sites and my stack as much as possible.
Since I'm already hosted in Netlify, I decided to use Netlify Functions for my serverless platform since that enabled me to maintain a single deployment strategy.
So what I wanted to do, is to have a single Netlify Functions endpoint that returns me the info on blog posts that my blog listing needs: mainly their title, slug, excerpt, date and, most importantly for this feature, tags.
The Implementation
Due to my workflow, there are three steps to this implementation. I could skip one step by calling Ghost directly from the Netlify Function but one reason I really like the static nature of the site is that it has less dependencies at run-time. I already introduce one via Functions, I don't want to introduce two at the same time.
Step 0: Create a Function
Using Netlify Functions was a great experience. Thanks to their CLI toolkit (mainly functions:create
, dev
and deploy
), I was able to set everything up in a few commands.
First, I ran netlify functions:create
to use a guided interactive process to get a template file in a proper folder. In my case I created a netlify/functions/
folder in the root of my project.
Second, I added a section to my Netlify config file:
[functions]
directory = "netlify/functions/"
To make sure my skeleton function was working, I booted up a dev server with netlify dev
and tested it by running the function in /.netlify/functions/[function_name]
Step 1: Fetch posts
I have a very manual workflow for writing and publishing my blog posts: I don't use any automatic integrations but always fetch my posts by hand (using custom Node script) and deploying by hand. For updating the serverless function, I created a second Node script that fetches all the needed info for my posts from Ghost and writes them into a Javascript file.
The main logic looks like this
api.posts.browse({ limit: "all", include: "tags" }).then((posts) => {
const mainPosts = posts.filter((post) => !isGaming(post) && !isDraft(post));
const taggedPosts = mainPosts.map((post) => {
const { title, slug, tags, excerpt } = post;
return {
title,
slug,
excerpt,
tags: tags.map((tag) => tag.name),
};
});
const template = `module.exports = ${JSON.stringify(taggedPosts)}`;
fs.writeFileSync("netlify/functions/get_posts/posts.js", template);
});
I'll run this everytime I make changes to my blog post tags in Ghost.
A one downside is that this approach currently only shows blog posts written in Ghost and none of my old Markdown posts. I'll maybe fix that when I make a larger rewrite of my website one day.
Step 2: Netlify Function
Now that I have all my post info in a Javascript file, hosting that to the caller via a serverless function is simple: read the data and return it.
const posts = require("./posts");
const handler = async (event) => {
try {
return {
statusCode: 200,
body: JSON.stringify({ posts }),
};
} catch (error) {
return { statusCode: 500, body: error.toString() };
}
};
module.exports = { handler };
Now, after each deploy, I can call a single endpoint that returns the newest data and use that to filter posts in the blog.
Step 3: Add filtering option to website
In my blog.njk
, I added a list of buttons for each category I wanted to include. It's hardcoded since I figured I mainly want to showcase certain tags.
When the page is loaded, I fetch all the posts from Netlify Functions and store them.
If a user clicks the "All" category, I simply do a page refresh to return to the original list of all posts, divided by years and months. If they click any of the actual tags, I filter the posts and only show the ones with relevant tag.