NoLoop Dev Log — Migrating the Backend from NestJS to FastAPI
One big day on NoLoop: kicked off the NestJS → FastAPI migration with 4 verified PRs, planned 14 issues of roadmap, deployed the admin dashboard, and earned some hard-won pgbouncer and timezone lessons.
Big day on NoLoop. Here's everything that shipped.
Kicked Off the Backend Migration: NestJS → FastAPI
I decided to move the entire backend to Python — FastAPI + SQLAlchemy 2.0 async — so the whole data + AI layer lives in one ecosystem. One rule for the migration: the frontends must not notice. Same routes, same response shapes, same JWTJSON Web Token — a compact, signed token carrying claims (such as a user ID and an expiry time) that a server can verify without a session lookup. contract.
Four PRs merged today, each verified against the live system before merging:
PR #1 — Scaffold: FastAPI app, pydantic-settings config, async engine on Supabase's pooler, and a /health endpoint with a real DB round-trip.
PR #2 — Models: all 12 tables + 11 enums mapped 1:1 onto the existing Prisma-created schema — zero DB migration, verified by querying every table live.
PR #3 — Auth: signup/login/me, JWT with an identical payload, and passlib verifying the old bcryptjs hashes so existing users keep logging in. Error bodies are NestJS-shaped.
PR #4 — Org + Admin: ran both backends against the same DB with the same tokens and diffed responses — 8/8 byte-identical, including 401/403 error shapes.
PR #5 (claims + AI client) is code-complete and pushed — read-parity passed 7/7 (all 56 claims, full detail shapes, public tracking) — held open until the mutation tests run.
Gotchas Earned Today
PgBouncerA lightweight connection pooler for PostgreSQL that lets many clients share a few database connections. In transaction-pooling mode, session features such as prepared statements need care. in transaction mode needs NullPooland unique prepared-statement names — statement_cache_size=0 alone is not enough.
Naive datetimes without a trailing Z silently shift every timestamp by 5½ hours in the browser.
Error bodies are a contract. My frontend reads data.message, so FastAPI now speaks Nest's error dialect.
Planned the Whole Road Ahead — 14 Detailed GitHub Issues
Everything from finishing the migration to production hardening is now on the board:
Async task queue — retries, idempotency, DLQs
The RAGRetrieval-Augmented Generation — fetching relevant passages from a document store at query time and handing them to a language model as context, so its answer is grounded in those sources. centerpiece — pgvectorA PostgreSQL extension that adds a vector column type and similarity search, so embeddings can live alongside ordinary relational data., hybrid retrieval, citation-grounded answers, an eval set
DB scaling + a backup restore drill, k6 load testing, Redis caching
Deployed the Admin Dashboard 🚀
Live on Vercel with a custom domain set up via the CLI: noloop-admin.sandeepp.in. Proper README + MIT license added. All changes shipped through 3 small branch → PR → merge cycles.
The Multi-Repo Scare
Also survived a classic: I accidentally worked in a fork of the team's org repo instead of my personal one. Caught it, swapped to the right repo, redid the work cleanly — and wrote down the rule:
Check git remote -v before pushing. Always pass --repo to gh pr create.