Tuesday, May 14, 2024
Superpowering the Bulletin with a Headless CMS
Having the ability to leverage tools such as Next.JS and Sanity CMS provide the best balance of control and abstraction to write and ship out a personal blog within a few days.
Sean Fong
@seancfong
A high-level Architecture
The bulletin was built from the gound-up with the goal of customizability and maintainability.
Relatively speaking, the architecture is very minimalistic. Yet, it enables me to edit and write posts without having to touch code when I don't need to. At the same time, I can edit the web app at any time to add new features to achieve the level of customization I want.
Control and Abstraction
There's so many options out there that lets anyone create a personal website. They also fall on a wide spectrum of "control" and "abstraction". Simply put, services like Wordpress and Wix abstract the manual work of writing code and let the user focus on creating the content. Usually, the tradeoff of these comes at the cost of less control over customization (and the cost of them too!)
On the flip side, we also have the choice of going all in on developing everything from scratch. But going too deep in the rabbit hole leads to prolonged development and churns projects to a halt if it goes on for too long.
Next.JS as a SSR framework
My stack of choice when it comes to creating statically-generated websites is Next.JS paired with a headless CMS. This embodies the "Jamstack" architectural paradigm, where low-velocity data such as markdown of a blog post are separated from the user interface.
Jamstack is an architectural approach that decouples the web experience layer from data and business logic, improving flexibility, scalability, performance, and maintainability.
Server-side rendering is clearly the winner for such a use case, and Next.JS has become the standard because of its ease-of-use for those familiar with React, as well as the control developers have to cache and invalidate static paths.
Sanity as a Hackable Headless CMS
It comes to no surprise that a Content Management System (CMS) takes on the responsibility of storing data that is directly accessible. It also serves as a higher layer of abstraction compared to hosting a database and designing a procedure to store markdown.
Compared to other Content Management Systems such as Wordpress, however, headless CMS services are agnostic of the user interface. This separation of concerns allows it to exist independently of any React code.
Sanity CMS is hackable. Compared to other headless CMS services, Sanity allows developers to define schemas as code. Not only is this easier to maintain in version control, but it also supercharges development speed by saving the time it takes to click around menus and form entries.
How it works
The CMS Schema
Each post has a unique slug that is used to identify different posts from others. For example, the slug for this post is superpowering-the-bulletin-with-a-headless-cms.
The body of each post is a markdown block type. This means that writing each post is as easy as writing a Notion document!
Next.JS on the server
The server fetches a single post's data on the dynamic route /bulletin/post/[slug], with [slug] being replaced with the post's actual slug. There's two options of what happens:
1. Cache Hit
- Next.JS sends the cached HTML to the user.
2. Cache Miss
- Next.JS first makes an API call to Sanity during request time, renders the page on the server, caches it, and then sends the rendered HTML to the user.
Obviously, cache misses end up in longer page loads. Because the Sanity project isn't configured to be distributed through a CDN, this can take some time.
As such, the server has been configured to always cache every response per dynamic route. Next.JS has its own optimizations to also improve the page load times for first-time loads.
Webhooks and Revalidation
So the next problem is being able to update the bulletin whenever a post is added. Sanity, however, supports webhooks that trigger whenever content is changed. Under the hood, it's just a POST request being sent to an API route, with the body containing the slug of the post.
The API route that handles this webhook is then able to invalidate the cache based on the slug in the body. This means that the next fresh request will fetch the newly updated data from Sanity for the blog post.