Building My Own Backend for DeckDen Taught Me This

How building DeckDen with Next.js, FastAPI, and PostgreSQL taught me that owning every layer of your backend changes how you actually understand your entire stack.

7 min read

Building My Own Backend for DeckDen Taught Me This

My last project ran on Supabase, and it worked — auth, database, permissions, all handled by a platform doing the heavy lifting behind the scenes. That project is actually what pushed me to build DeckDen differently: I wanted to know what was really happening under all that convenience. So for DeckDen, I built the backend myself — FastAPI, PostgreSQL, SQLAlchemy, no platform doing the hard parts for me. That decision ended up teaching me more about how a real stack works than anything else I've built.

What I thought going in

Coming off a project where auth was basically a function call, I figured building my own backend meant writing a few routes and a schema, then wiring them up to a frontend. I didn't think it would be that different. I was wrong almost immediately.

What DeckDen actually made me learn

Once I was writing the auth, the database, and the deployment myself, an entire layer of decisions showed up that I'd genuinely never had to think about before:

  • Password hashing — choosing bcrypt specifically because it's slow on purpose, and understanding why that slowness is the entire point of storing a password safely

  • Session management — issuing my own JWTs, picking an expiry window, deciding exactly what data lives inside the token

  • Password resets — generating a high-entropy token, hashing it differently than a password (fast and deterministic, not salted, because a reset lookup needs an exact match), and making sure a used token can never be replayed

  • Cascading deletes — when a user deletes their account, making sure their decks, cards, and reset tokens actually disappear with them instead of becoming orphaned rows

  • CORS — learning that "the frontend can talk to the backend" isn't automatic, it's a per-environment configuration decision, and getting it wrong either breaks the app or opens it up

  • Environment and secrets — SECRET_KEY, database URLs, API keys, all needing to live differently in local Docker versus production, none of it hardcoded

  • Deploying two real services — a Railway Postgres instance and a Railway API service that have to be told how to find each other, instead of one dashboard doing it for me

  • Testing that it actually works — writing pytest and Vitest suites, because clicking through it once was never going to be good enough

Why this actually mattered

The password reset flow is the clearest example of what changed in how I think. My first instinct was to hash the reset token exactly like a password, because "hashing is hashing." It took sitting with why bcrypt exists — the deliberate slowness, the random salt — to realize that same salt makes an exact-match database lookup on a token impossible. Getting that wrong, and then figuring out why it was wrong, taught me more about security primitives than any tutorial had.

The same thing happened with CORS errors that only showed up in production, and with a cascading-delete bug that forced me to actually understand what cascade="all, delete-orphan" was doing in SQLAlchemy instead of just pasting it in because it looked right. Every one of those moments had the same shape: I thought I understood something, and building it myself proved I didn't — yet.

What this opened up for me

DeckDen didn't just teach me FastAPI or PostgreSQL. It taught me to actually own the pieces I used to treat as invisible — the hashing, the tokens, the CORS config, the deploy. Now when I look at an env variable or a cascade rule, I know why it's there, not just that it needs to be.

If you're a junior dev who's only ever shipped on a platform that handles the backend for you: that's not wasted time, but it's not the full picture either. Build one thing where you own every layer yourself — badly, at first. You'll find out fast exactly which parts you only ever understood secondhand.