I tried to build my own microblogging platform
A few months ago, I posted the following:
I’ve started building an end-to-end microblogging platform for myself — something I’ve thought about doing for a while! Unfortunately that means this is likely my last month on Micro.blog.
With this post, I’m actually returning to Micro.blog — so what happened?
Why build my own platform in the first place?
For the uninitiated (and to put it simply), Micro.blog is a lovely indie microblogging platform that puts you in control of your content. Leveraging Hugo on the backend, every post is published to your own (hosted by Micro.blog) website.
The way I use Micro.blog is different than most. I effectively use it as a CMS — my custom Hugo template hackily creates static JSON files (as opposed to HTML) for each post, which acts as a pseudo-API for the microblog here on my built-with-Astro website.
My desire to build my own platform was driven by a few factors:
- I was already doing things in much my own way.
- I wanted enhanced image format support (JXL, AVIF, etc.).
- I thought it would be fun to build and experiment with a few technologies.
Abandonment
I was initially very excited and motivated to build this project, and I actually built most of the backend in just a couple days. But, as it so happens, I was hit with a whirlwind of mostly negative personal life events, and I found it very difficult to find the time and motivation to continue work on the project.
During my microblogging hiatus, I made a couple realizations that ultimately helped me abandon the project:
- I realized microblogging is not that important to me — I was easily able to go without posting for the last eighty-odd days. And if microblogging isn’t all that important to me, then it doesn’t make a whole lot of sense to build all this for it.
- Micro.blog is a really great platform already. I can already get most of what I want from it, and the things I can’t get are frankly not worth the effort of building my own platform.
- I should put more focus on my blog.
So, I’m happily returning to Micro.blog. I don’t intend to use it as actively as I did before, instead focusing more on my blog, but we’ll see.
Development
As aforementioned, I did actually get quite close to completion — which made it all the more difficult to abandon the project. I largely completed the backend, and I had a good start on the frontend.
For the backend, I made complete use of Bun and its bundled features, namely Bun S3 and Bun SQLite. Bun is brilliant to work with, and made building a performant backend and its associated APIs a breeze.
Posts were stored in SQLite, and I interfaced with the database using Drizzle ORM (which leverages Bun SQLite). My schema looked something like this:
export const postsTable = sqliteTable(
"posts",
{
id: text().$type<ReturnType<typeof postsNano>>().notNull(),
revision: int().notNull().default(0),
checked: int({ mode: "boolean" }).notNull().default(true),
draft: int({ mode: "boolean" }).notNull().default(false),
datePublished: text().$type<Date>().notNull(),
dateModified: text().$type<Date>(),
title: text(),
summary: text(),
tags: text({ mode: "json" }).$type<string[]>(),
images: text({ mode: "json" }).$type<imageMetadata[]>(),
ogs: text({ mode: "json" }).$type<ogMetadata[]>(),
relay: text({ mode: "json" }).$type<platformRelay>(),
content: text().notNull(),
},
(table) => [primaryKey({ columns: [table.id, table.revision] })],
);
I also built an image processing pipeline with Sharp. The pipeline converted images to both AVIF and JXL formats, and uploaded them to a remote S3-compatible storage provider. The pipeline returned the necessary metadata for each image, which was stored in the database.
Bluesky cross-posting was the last backend feature I worked on. Using the ATProtocol TypeScript SDK, I was able to relay posts to my Bluesky account.
For the posting frontend, I went with Next.js. In addition, I used Tanstack Form for form management, and Tanstack Query for data fetching. I had large ambitions for the frontend, but I only got as far as a basic posting form. Nonetheless, I was pleased with how what I had built came together.
I may open source the code — at least the backend — at some point. I think it has some value, though it’s pretty specifically built for my own use case.