Moving My Blazor WebAssembly Blog to Hashnode Headless CMS
A while back, I decided to start blogging. My goals were simple: to document what I'm working on, how I'm currently developing the projects/products I work on daily, and why I prefer using one method over another. At the same time, I also aimed to share these methods with a broader audience to help the community that has supported me throughout my career.
So, I wanted a blog and built one. Despite the numerous blogging platforms available, I saw this as an opportunity to explore the new Microsoft frontend framework Blazor. This focus on learning Blazor led me to put aside blogging on platforms like Hashnode at that time.
I began with a clean Blazor Web Assembly project and developed several components, styles, and pages. It became clear that I needed a simple Content Management System (CMS) since building one from scratch was not on my mind. After all, my main goal was blogging, and I wanted to avoid unnecessary technical complexities.
Git-based CMS
Digging through the internet, I stumbled across Git-based CMSs. For me, the possibility of simply having a small configuration file to define the basics of my content model and media location, along with the convenience of my posts being available on Git for easy access and updating, got me interested in trying one of these CMSs.
At the time of selection, I opted for Forestry. It was quick to set up, and the documentation was comprehensive enough that after a couple of days, I was up and running. An important factor in this decision was that I didn't have to host the CMS; a free tier of the service was available, so I could get started easily. I connected my CMS with my blog's Github code repository, and the blogging journey began...
The Fall of the Forestry
The blog was up, the CMS was working, and everything seemed ready for more content.
Well, almost... I wasn't quite used to blogging yet, there wasn't a routine or a planned schedule for it. I struggled to get started with life happening and didn't put in enough effort for a while to add new content regularly.Some topics made it to an ideation board but not into writing, at least not yet.
Getting back at it
One day I feel ready to write a blog post again, pick one idea from the board, throw in some bullet points to structure my thoughts, open the Forestry CMS website, and ...
Forestry was decommissioned in favor of a new product, and a migration guide was launched, but there was no way to keep using Forestry and continue writing my blog posts.
I was back to square one and needed a new CMS solution, following the same principles:
Easy to configure
No hosting necessary
It could be integrated with my Blazor WebAssembly blog
Support importing my previous blog posts
Ready to use
Hashnode Headless News
Before adopting Hashnode Headless, I already had an account on this amazing blogging platform and was enrolled in the newsletter. It was through that newsletter that I learned about Hashnode launching Hashnode Headless and the opportunity to explore this product and its available GraphQL API.
Hashnode provides an API Playground that I used to understand how this API works and what it would take to connect my blog to it. After some time, I was confident that I could make this API work with my blog, so I was excited to continue experimenting with it.
I returned to Hashnode's backend, made some simple configurations with my blog domain, and imported the very first post I had written on my blog into Hashnode. I aimed to write some C# code and create a small Proof of Concept (PoC) to fetch my blog post.
using GraphQL;
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.SystemTextJson;
using System.Text.Json;
var graphQLEndpoint = "https://gql.hashnode.com";
var publicationId = "my-pub-id";
var postSlug = "my-post-slug";
var httpClient = new HttpClient();
var graphQLClient = new GraphQLHttpClient(new GraphQLHttpClientOptions { EndPoint = new Uri(graphQLEndpoint) }, new SystemTextJsonSerializer(), httpClient);
var query = $$"""
query {
publication(id: "{{publicationId}}") {
id
isHeadless
post(slug: "{{postSlug}}") {
id
slug
tags {
name
}
title
publishedAt
coverImage {
url
}
brief
url
content {
markdown
}
readTimeInMinutes
}
}
}
""";
var graphQLRequest = new GraphQLRequest
{
Query = query
};
var response = await graphQLClient.SendQueryAsync<dynamic>(graphQLRequest);
The PoC revealed that achieving the integration cleanly was possible. The challenge now was importing all blog posts and refactoring the remaining code to use Hashnode's GraphQL API.
Mapping to my current content models was also crucial for this migration since I did not want to disrupt the blog availability during the switch.
Upon completing these tasks, the next crucial phase involved revisiting my blog on Hashnode to identify and address any potential errors or issues that may have arisen during the migration process. This thorough review was necessary to ensure the smooth functioning of my blog on the new platform.
Challenges and Breaking Changes
During the review of the blog migration, I discovered some issues where my content wasn't displaying correctly. The main ones I needed to fix before merging my changes and fully applying the new CMS were:
Broken styles for code blocks
Images with inline styles not showing up
Lack of captions on images
Inability to set posts and pages on different subdomains
The first two issues stemmed from how I converted markdown to HTML. To address these, I made the following changes to my blog code:
Created new styles for <pre> tags used for code blocks
Removed any inline styles from images before converting markdown to HTML
The inline styles appeared when I aligned images in the Hashnode editor.
In my previous CMS, I could add titles to each image, which were then converted to a <figure> with a <figcaption> tag. This feature is currently not available in the Hashnode editor. Trying to set it in the raw editor led to the image not displaying at all. For now, the solution was to remove the captions from my blog.
The last challenge with a single entry for the blog domain caused essentially any post url returned from the API to be incorrect, lacking the /post/ prefix. Pages will receive the correct url since they are located at the root of my domain. The way forward was to stop using the URL field for my posts, but two side effects occurred from this decision: the Hashnode "go to post" button will redirect to the 404 page, and building, for example, an RSS Feed out of the Hashnode API information will require additional mapping to append /post/ to any post URL.
Enhancements that result from the migration
There were several notable enhancements resulting from this migration that have significantly improved my blogging experience the key improvements for me are:
Better markdown editor π
Possibility of having code blocks marked with a specific language (C#, Json, XML) π¨βπ»
Image Content Delivery Network (CDN) and optimization π
Distribution on Hashnode network πΈοΈ
The upgraded markdown editor has improved the writing experience, making content creation smoother. Being able to specify programming languages for code blocks enhances readability and enables code highlighting.
Adding a CDN has boosted the loading speed and performance of images in blog posts. While this wasn't a priority initially, it's a valuable addition that was missing in my previous CMS.
Expanding distribution across the Hashnode network is a significant advantage. It boosts the visibility and exposure of blog content to a larger audience within the Hashnode community.
Conclusion
Integrating my Blazor WebAssembly blog with Hashnode Headless CMS has been a rewarding experience. Despite facing challenges during the transition, the enhancements and improvements gained from this move have significantly improved my blogging journey. With a better markdown editor, improved code block markdown, and optimized image delivery, and distribution on the Hashnode network, I am looking forward to a new chapter of more regular and engaging content creation.
Subscribe to my newsletter
Read articles from Tiago Fonseca directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Tiago Fonseca
Tiago Fonseca
Hi there π Iβm Tiago Fonseca, a software engineer from Portugal. I started coding when I was about 15 years old at school. Since then I've mainly worked with .net! From web forms to the latest thing there is at .net stack. Over the past years, I've designed, developed, and deployed various projects using different technologies. I'm a believer in continuous learning and that there is always something that we can make better. Why this blog exists For more than a decade I had been gathering knowledge about the art of software engineering. Although I have mentored others in person and online, I felt I could contribute more to the community by sharing this knowledge in blog posts broadening the audience. This blog reflects only opinions of my own and is not related to my current or previous employers.