From "Learning Blog" to True Blogger API

simplifiedd
simplifieddPro
9/10/2026 · 162 views

# From "Learning Blog" to True Blogger API: Building a Platform That Writes and Talks

Every developer has that one side project that starts small and quietly becomes the thing they actually care about. For me, that project is True Blogger API.

It didn't start with a grand plan. It started as `learning-blog-api`, a fairly ordinary idea: a place to post blogs, with roles for users and moderators, and a small public API bolted on almost as an afterthought. For a while that's exactly what it was. But somewhere in the middle of building it, I sat down and actually thought about what I was making, and realized the name was underselling it. This wasn't a "learning" project anymore. It was a real blogging platform where every single account, free or paid, gets a genuine REST API to their own content. That's not a common pairing. Most blogging platforms treat their API as an internal implementation detail, if they expose one publicly at all. I wanted the opposite: writing and building are the same feature, not two separate products duct-taped together.

So I renamed it. True Blogger API. And once the name matched the vision, the rest of the build started making a lot more sense.

## The part nobody tells you about: the math has to work

It's easy to build features. It's much harder to figure out whether the thing you're building can actually sustain itself without either going broke or nickel-and-diming the people who use it.

I didn't want to guess at pricing. I sat down and worked through the real numbers: what a MongoDB Atlas cluster actually costs as data grows, what actually drives that cost (it's not user count or blog text, it's uploaded images, which are stored as raw binary and can be fifty to a hundred times heavier than a full post), and what a realistic growth curve looks like for a platform that starts with one known cohort of users and an unpredictable trickle of everyone else.

Then came the part that actually surprised me: Stripe's fees. At a dollar or two a month, the flat thirty-cent-per-transaction fee eats a third of the charge. Billing annually instead of monthly almost completely solves that problem, because that fixed fee only hits once a year instead of twelve times. That one realization reshaped the entire pricing page. I landed on $25 a year, or $3 a month for people who'd rather pay small and often, priced deliberately to nudge people toward the annual plan where the economics actually work in everyone's favor.

I also built in something I cared about more than the revenue itself: a rolling cohort deal. Students from a specific school get free professional-tier API access for a real, meaningful stretch of time, tied to their school email domain, expiring and renewing on its own with no manual bookkeeping. The idea was never to squeeze the earliest, most loyal users. It was to make sure the platform pays for its own hosting through the people who eventually age out and want to keep using it, not through the people who gave it a shot first.

## Building for an audience that includes minors

Once I accepted that this platform would realistically be used by teenagers, not just working developers, the whole safety posture had to change. A word filter and a suspend button aren't a moderation system, they're a start.

So I built an actual pipeline: reporting on posts, comments, and accounts, a freeze state separate from outright removal so a moderator can pull something out of public view without destroying it, timed suspensions that lapse on their own, and a ban process that requires two sets of eyes before it's permanent, unless you're the account with final authority. Appeals get a real dedicated review queue instead of a chat bubble bolted onto an admin table.

Age-gating went through two real iterations. The first pass greyed out certain profile options for anyone under eighteen. It worked, but it still showed minors a door they weren't allowed to open, which felt wrong the moment I looked at it honestly. The second pass removed those options from view entirely for anyone flagged as a minor, especially anyone matched to a school's email domain, which I ended up thinking of as "out of sight, out of mind" rather than "visible but locked."

Underneath all of that, I added application-level encryption for emails and phone numbers, on top of whatever the database provider already does at rest, specifically so that a compromised connection string alone isn't enough to read anyone's real contact information in plain text. Building that meant re-touching login, registration, password reset, ban enforcement, and admin search all at once, because every one of those paths had been quietly matching on plain email strings for months. It was tedious. It was also the kind of work that doesn't show up as a feature screenshot but matters more than almost anything else I built.

## The bugs that taught me the most

A few bugs stick with me more than any feature did.

Early on, I kept hitting a maddening pattern: I'd add a new field to a schema, write to it, get a clean 200 response back, and then the data just wouldn't be there on the next read. Three separate times I chased this before I recognized it as the same root cause: a schema change doesn't take effect on a hot reload, only on a full server restart, because the framework caches the compiled model in memory. Once I knew to look for it, it stopped costing me time, but the first three times were genuinely confusing.

Later, a validation rule meant to stop malicious image URLs started silently blocking perfectly normal saves, because it was re-checking a field every single time a post was edited, even when that field hadn't actually changed. Anyone touching an old post with a legacy image link couldn't save anything at all, including just flipping a post from private to public. The fix was small once I found it: only validate a value when it's actually different from what's already stored. I found and fixed that exact class of bug twice, once for blog posts and once for profile avatars, because I hadn't generalized the lesson the first time.

The one that really humbled me came from Google sign-in. I'd built account banning and suspension checks against every login path, tested them thoroughly, and thought the coverage was solid. Months later I discovered that Google's login protocol identifies itself under a different internal label than the generic one my code was checking for, which meant every single Google login had been silently skipping ban checks and account linking the entire time. It worked in every test I'd run because I'd never happened to test a banned account signing in with Google specifically. That one taught me to stop trusting that "I tested the login flow" means what I think it means, and to actually enumerate every branch instead.

And then there was launch week's real gut-punch: every single blog post detail page was returning a server error in production, completely silently, because a library I depended on for rendering rich text tried to load a piece of itself in a way the server's runtime environment couldn't handle. It worked flawlessly on my own machine. It worked in every local production build I ran. It only broke on the actual hosting platform's serverless environment, which meant the platform's core feature, reading a blog post, had been broken since the moment it went live, and nobody had noticed yet. Tracing it down meant reading a raw stack trace instead of guessing, swapping the underlying rendering library for a lighter one with no shared dependencies, and then rebuilding the sanitization rules by hand from the actual rendering code instead of trusting a generic library to get it right. I didn't consider it fixed until I ran a real production build locally and loaded the exact post that had been failing.

## Launch day

The day I actually pushed this to a real GitHub repository and deployed it, the codebase had been sitting on essentially two scaffolding commits despite weeks of real work. Everything had lived locally the whole time. Getting it live meant going through the unglamorous checklist that never makes it into a portfolio screenshot: making sure the `.gitignore` wasn't accidentally excluding the safe example environment file, confirming the repository was genuinely private with an unauthenticated check rather than trusting what the dashboard showed me, generating a fresh production signing secret instead of reusing my local one, and setting up a second OAuth application for production since GitHub only allows one callback URL per app.

Then the domain refused to connect cleanly, and a direct request from outside the dashboard showed a broken TLS handshake even though the platform's UI cheerfully said "Deployed." Then, once the domain was actually working, the site came back with a database permissions error, because the connection string I'd pasted into the hosting provider was missing the database name entirely, silently falling back to a default database my scoped production credentials had zero access to. Both of those took real troubleshooting, not guesswork, because trusting a dashboard's status indicator instead of verifying the actual behavior would have meant walking away thinking everything worked when it didn't.

By the end of that day, it did work. A real domain, real authentication across three providers, a real database, and a fully wired billing pipeline, verified end to end rather than assumed.

## Where it stands now

True Blogger API is live. It's a blogging platform first, but every account on it, free or paid, has a real API behind it. It has moderation built for an audience that includes people too young to have a credit card. It has pricing that was worked out from real infrastructure costs rather than picked out of the air. And it's still getting sanded down, one real bug and one real decision at a time, days after launch, not just before it.

The plan from here is the part I'm most looking forward to: actually writing on it. Not seed data, not test posts. Documenting the rest of this build, in public, on the platform itself, the same way this post exists to do right now.

Comments

Log in to leave a comment.