Last updated on

Starting Full-Stack From What I Already Know


I've built plenty of front-end applications backed by a CMS — that's familiar ground. What's less familiar, lately, is the other half: the server, the data model, the admin tooling, the deploy pipeline. If I'm going to move from front-end specialist to full-stack, I need reps on that half. I decided the fastest honest way to get them was to start from a domain I already understood rather than learn a new stack and a new problem at the same time. My own site was the natural place to do it, and Payload CMS was where it started.

Where it started: two apps, on purpose

The first version of this site was two separate applications: an Astro frontend that statically fetched content from a Payload backend over its REST API. That's not a naive setup — it's a genuinely reasonable one for a mostly-static blog and portfolio. Astro ships close to zero JavaScript, the two apps deploy independently, and Payload's REST API is exactly what it's built for. If your goal is a fast, simple, mostly-static site, decoupled like that is a defensible place to stop.


I didn't stop there, because staying there wasn't going to teach me anything I didn't already know.

Why Payload, specifically

I've worked with CMS-backed apps before — just not recently, and not on the current generation of tooling. Payload appealed for reasons that had nothing to do with being trendy: it's TypeScript end to end, the content model is defined in code instead of a vendor's UI, and it's self-hosted against a database I control instead of a SaaS product with per-seat pricing. None of that was a hard requirement — I could have picked Sanity, Strapi, or a dozen others and learned similar lessons. Payload was the one that matched how I already think about structuring an app, which meant I'd spend my time relearning the CMS layer instead of also fighting an unfamiliar paradigm on top of it.


That's the actual strategy: when you're rebuilding a muscle, start with the exercise you already know the form for, not the one that's hardest.

Collapsing two apps into one

More recently I made a second decision on the same project: I moved Payload from a standalone backend that Astro called over HTTP into a single Next.js app, with Payload mounted directly inside it. Same content model, same admin UI — but the frontend and the CMS now share a process, a deploy, and a database connection instead of talking over the network.


Two things drove this. First, it's Payload's own recommended pattern as of v3 — mounting it inside Next instead of running it standalone is the supported path, not a workaround. Second, and more honestly: a single full-stack Next.js app — App Router, server components, a frontend reading straight from a database through a local function call instead of gluing two apps together over HTTP — is a far better demonstration of full-stack ability than a static site calling someone else's REST endpoint. That's not a technical merit. It's a positioning one, and I'd rather say that plainly than dress it up as pure engineering.


The technical upside is real too, if smaller than the positioning one: no more HTTP hop between frontend and CMS, no CORS configuration, no separate URLs to keep in sync across two deploys. Fetching a post used to mean an HTTP request to a REST endpoint; now it's an in-process function call against Payload's Local API. One fewer network boundary to reason about.

What got worse

It would be easy to stop at the upside. It's more useful — to me, and to anyone reading this to gauge how I make decisions — to say what got worse.


This is no longer a static site. Payload needs a persistent Node server: a live Postgres connection, an admin UI, REST and GraphQL routes all running continuously. The whole app, including the plain content pages, now deploys as a Node/serverless app rather than static files on a CDN. If pure-static hosting for the public site ever matters again — cost, simplicity, an outage-proof front end — that means splitting back into two apps.


And the more important one: this change doesn't move my income goal forward by itself. It's a reasonable, low-risk place to build full-stack reps, because it's a personal project and nothing breaks if I get it wrong. But reps aren't clients. The actual work of getting to freelance or consulting income is client acquisition and positioning, and no amount of architecture on my own portfolio site substitutes for that. I'm trying to stay deliberate about not confusing the two.

What's still open

The migration works, but it isn't finished. Publishing a post in the admin UI doesn't yet trigger a rebuild — there's no deploy hook wired up, so content changes need a manual rebuild for now. Media uploads still default to local disk, which won't survive a deploy to an ephemeral filesystem, so that needs a real storage plugin before real images go through it. I'm naming these because a case study that only shows the finished, polished version isn't an honest one — and isn't especially useful to anyone deciding whether to work with me.

What's next

This post covers the decision: why a CMS, why Payload, why one app instead of two. A more implementation-focused follow-up is coming — what the content model actually looks like, how Payload's Local API and rich text ended up wired into the frontend, and the parts of the setup that are easier to show than to describe. If the decision-making is what interests you, this post is the whole story. If you want to see the code, that one's next.