vM.

Django to FastAPI: Moving a Team Without Chaos

Author
Vishal Maurya
Published on
Reading time
4 min read

Overview

Someone on the team wants to move the Django backend to FastAPI. Maybe the API is mostly JSON, async looks attractive, or typed schemas are appealing. Before you start, it helps to separate the real problem from the framework label, then migrate in a way that does not freeze feature work.

1. When Not to Migrate

Django gives you an ORM, migrations, admin, auth, forms, and a mature ecosystem. FastAPI gives you a typed, async-friendly API layer and leaves the rest for you to assemble. Do not migrate if:

  • Your pain is slow queries, missing indexes, or poor caching. A new framework will not fix those.
  • You rely heavily on Django admin, templates, or forms.
  • The team has little Python async experience and the app is not IO-concurrency bound.
  • You cannot afford a period of running two stacks.

Django REST framework or Django Ninja may close much of the gap you are feeling at a fraction of the cost. Be honest about what is missing today.

2. Migrate Incrementally, Not by Rewrite

The least risky route is the strangler pattern: put FastAPI beside Django, route specific endpoints to it, and move capability over time. New, self-contained features are good first candidates, as are high-traffic read endpoints.

Both services can share the same database initially. Keep one source of truth for schema changes, and agree on which system owns migrations for which tables. A reverse proxy routes by path prefix.

3. Project Structure

Replace Django's implicit conventions with explicit ones. A layout that scales for a team:

app/
  main.py            # FastAPI app and router registration
  core/              # settings, security, dependencies
  db/                # engine, session, base models
  users/
    router.py
    schemas.py       # Pydantic models
    models.py        # SQLAlchemy models
    service.py       # business logic
  billing/
    ...

Keep routers thin and put business logic in service functions that do not import the web framework. That makes the logic testable and reusable.

4. ORM and Migrations

The usual choice is SQLAlchemy with Alembic. Alternatives exist, including SQLModel and Tortoise, so evaluate maturity and team familiarity.

Two points deserve attention:

  • Session handling is explicit. You create a session per request through a dependency, and you decide when to commit.
  • Alembic does not auto-detect everything. Review autogenerated migrations before applying them.
from collections.abc import Iterator
from fastapi import Depends, FastAPI
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker

engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(engine, expire_on_commit=False)


def get_db() -> Iterator[Session]:
    with SessionLocal() as db:
        yield db


app = FastAPI()


@app.get('/users/{user_id}')
def read_user(user_id: int, db: Session = Depends(get_db)):
    ...

While both frameworks share a database, avoid having Django migrations and Alembic both change the same tables. Pick an owner per table and document it. Consider reflecting existing tables first rather than rewriting models all at once.

5. Sync or Async Database Access

Using async def endpoints with a sync driver blocks the event loop. FastAPI runs plain def endpoints in a thread pool, which is safe for sync SQLAlchemy. Async SQLAlchemy with an async driver is an option when you need it, but it changes how you handle lazy loading and sessions. Start with sync if the team is new, and move to async where measurements justify it.

6. Background Jobs, Auth, and Admin

  • Background jobs: FastAPI's BackgroundTasks runs in-process after the response and is not durable. Keep Celery, or adopt a queue library such as Arq or Dramatiq, for work that must survive restarts.
  • Auth: Django sessions will not carry over automatically. Decide whether to share the session store, move to tokens, or keep Django as the identity provider during the transition.
  • Admin: FastAPI has no equivalent built in. Keep the Django admin pointed at the same database if internal tools depend on it, or budget for replacements.

7. Team and Process

  • Agree on conventions: linting, typing, test layout, dependency injection style.
  • Write contract tests against current API responses before moving an endpoint, and compare behavior after.
  • Migrate one vertical slice, learn from it, then continue.
  • Track what you expect to gain, such as latency or developer experience, and verify it.

Conclusion

A Django to FastAPI move can work well, but it replaces batteries with choices. Migrate gradually, own the ORM and migration strategy explicitly, keep durable job processing, and consider whether improving the existing Django app solves the actual problem.

If you are weighing this decision or planning a phased migration, I can help assess the tradeoffs and design the rollout. Contact me.

Additional Resources

  • FastAPI documentation
  • SQLAlchemy documentation
  • Alembic documentation