Skip to Main Content

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:

  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 = => {
    const { title, slug, tags, excerpt } = post;

    return {
      tags: =>,

  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.