jade@vandor.sx

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:

  1. 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.
  2. 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.
  3. 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.