
Curator API : Content Management
The Architecture
Every project in my professional stack reaches for TypeScript, Next.js, and NestJS. Curator API was built deliberately outside that comfort zone — a Python-first backend to stress-test my understanding of REST API design principles independent of the frameworks I already know well.
The domain is a content curation platform: authenticated users can submit URLs, the service auto-fetches the page title via HTTP scraping, and users can tag, organize, and discover content through a personalized feed. It's a bounded, realistic problem — complex enough to require relational data modelling, but not so large that architectural decisions get obscured.
The data model is the most interesting part. Content and tags share a true many-to-many relationship via a join table, managed entirely through SQLAlchemy ORM with Alembic handling schema versioning. The tag-follow system — where users subscribe to tags and receive a personalized feed — required a second many-to-many (users ↔ tags) and a feed query that joins across three tables with deduplication logic.
FastAPI was chosen deliberately over Flask or Django REST Framework — its Pydantic-native request/response validation, automatic OpenAPI schema generation, and async-first design map closely to patterns I know from NestJS, making the conceptual translation easier to reason about and document.
Authentication follows the same JWT + Passlib pattern used in production work — bcrypt-hashed passwords, signed access tokens, and a reusable dependency injection guard on all protected routes. Docker Compose orchestrates the FastAPI app and PostgreSQL instance locally, making the dev environment reproducible in a single command.
Strategic Methodology
Schema-first development — the PostgreSQL data model and Alembic migrations were written before any route was built. This forced deliberate thinking about the many-to-many relationships and feed query logic upfront rather than retrofitting. Each feature (auth, content CRUD, tagging, feed) was built and tested as an isolated module before wiring together.
Engineering Challenges
- Modelling the many-to-many content ↔ tags relationship cleanly in SQLAlchemy — avoiding the ORM's tendency to generate N+1 queries when loading a content item with its associated tags, solved with eager loading via joinedload.
- Designing the personalized feed query — joining content, tags, and user-followed-tags across three tables while deduplicating content that matches multiple followed tags. Written as a single optimised SQLAlchemy query rather than in-memory Python logic.
- Auto-fetching page titles from submitted URLs without blocking the request — handled with httpx in async mode, with a graceful fallback to a null title if the target URL is unreachable or returns non-HTML content.
- Translating NestJS guard/interceptor patterns into FastAPI's dependency injection system — understanding how FastAPI's Depends() chain replicates the constructor injection model was the steepest part of the learning curve.
Project Impact
"Built to learn — a deliberately over-engineered content curation backend exploring FastAPI, PostgreSQL, and Python-first architecture as a counterweight to a TypeScript-heavy professional stack."
Core Arsenal
Technical Log.
A high-fidelity breakdown of the build's architectural achievements and performance markers.
Synthesis
"A Python/FastAPI REST API backend for a content curation platform — featuring JWT auth, URL submission with auto title-fetch, many-to-many content tagging, tag-follow subscriptions, and a personalised feed endpoint. Python-first backend architecture"
Hard Evidence
Proves stack versatility — Python, FastAPI, and PostgreSQL alongside a professional TypeScript/NestJS background signals genuine backend depth, not framework dependency.
Schema-first discipline — Alembic migrations written before routes, enforcing intentional data modelling over ad-hoc schema evolution.
Many-to-many feed query engineered as a single optimised SQLAlchemy join — no N+1, no in-memory filtering.
Async URL title-fetching with httpx — non-blocking, gracefully degraded, production-pattern even in a learning context.
Docker Compose dev environment — single-command local setup, same pattern used in production-grade deployments.