My first experience using Django REST Framework (DRF) in a project was almost a decade ago. I've defeneded DRF philosophy it in code reviews, taught it to juniors, and built production systems that still run today. But in recent years, after trying FastAPI in different projects, I started feeling bored. Not of Django. I still love Django. I felt it from the verbose boilerplate, layers of abstraction that solved problems I no longer had amde me ask myself if I need this much complexity in one place?
Then I found Django Ninja. And after two years of using it in production, I finally understand what I was missing.
This post isn't about "DRF is dead" — it's not. It's about understanding where each tool shines, and why Ninja has become my default for most new projects.
Let me walk you through the landscape honestly.
1. Django in 2026
Let's start with something we can all agree on: Django is still fantastic. Here's why I still reach for Django first:
"Batteries included" actually matters. Authentication, admin interface, sessions, CSRF protection, security middleware — it's all there, tested, and working. No stitching together 14 Flask extensions or FastAPI plugins.
The admin interface is a superpower. For internal tools, MVP dashboards, or giving non-technical staff data access, nothing else comes close (I know there are alternatives for other frameworks, but still none feels like Django's Admin Panel).
The ORM is genuinely productive. To be honest, I love SQLAlchemy, and I hate some weird behavior of Django ORM, but no one can argue how well it integrates with the whole framework.
The ecosystem is enormous. I can put here a never ending list of great apps and modules for anything you need in your project.
But Django's built-in API tooling is not good.
If you've ever tried to build a real API using only JsonResponse and manual request parsing, you know the pain:
- Manual validation everywhere
- No automatic documentation
- Routing feels repetitive
- Error handling is ad hoc
That's why we reached for DRF in the first place. But DRF was built in a different age.
2. DRF: A Decade-Old Masterpiece Showing Its Age
Let me be clear: DRF is not bad code. It's brilliant code written for a different world.
The Era DRF Was Built For (2011–2015)
Back then, the hot API trends were:
- RESTful purity — Hypermedia as the engine of application state (HATEOAS), resource-oriented design, proper HTTP verbs for everything
-
Hypermedia APIs —
HyperlinkedRelatedFieldwas a feature, not a footnote - Browsable API — inspired by Django admin, it was revolutionary for debugging
- Class-based views — because function-based views were "too simple" those days!
- XML support — yes, that's how old we're talking.
What DRF Did Right
- Standardized REST APIs on Django — before DRF, everyone rolled their own. It standardized a lot of concepts in Dajngo ecosystem.
-
Built a mature ecosystem —
django-rest-auth,drf-spectacular,drf-nested-routers, etc. - Introduced serializers — which, love them or hate them, were a solid abstraction for their time
- Gave us ViewSets + routers — made CRUD APIs almost automatic
What DRF Struggles With Today
No official async support — and no announced plans. Django added async in version 3.0 (2019), then 3.1, then 4.0, etc. But DRF? Still sync-only. The workarounds are fragile.
OpenAPI docs require third-party packages —
drf-yasg(not very active at maintenance),drf-spectacular(good but still extra work). No built-in solution.Serializers are slow and verbose — pure Python, lots of reflection, and writing
Metaclasses for every model gets tedious.ViewSets + routers are magical — great when they work, a nightmare when you need to debug why your
destroyaction isn't honoring permissions.The browsable API is less useful now — frontends are separate. Postman, Swagger UI, and generated clients are the modern workflow.
DRF is like a well-maintained 2015 luxury car. Comfortable, known, reliable. Great engine, but terrible gas mileage compared to modern hybrids. Steering system is fascinating, but the navigation is outdated.
That doesn't mean throw it away. But maybe stop buying new ones.
3. FastAPI: The Shiny New Toy That Makes You Question Everything
Around 2018, FastAPI exploded onto the scene. And for good reason.
What FastAPI got right:
- Async by default — before Django even had solid async support
- Pydantic validation — type hints that actually validated your data at runtime
- Automatic OpenAPI docs — Swagger UI and ReDoc with zero configuration
- Incredible performance — as fast as Node or Go for many workloads
- Dependency injection — an elegant, testable, and intuitive design pattern
I built several microservices with FastAPI. I enjoyed every minute of it.
But here's what nobody tells you about leaving Django for FastAPI:
Batteries not included. You reach for SQLAlchemy — which is excellent, but different. And now you're also reaching for Alembic (migrations), and something for admin, and something for auth, and something for...
No built-in authentication system. You'll piece together OAuth2, JWT, sessions, or reach for
fastapi-users(good library, but not Django'sdjango.contrib.auth).You're building half of Django yourself. That's fine for a small microservice. For a maintained large application? That's technical debt disguised as "lightweight."
The honest take: FastAPI is fantastic for microservices, APIs that need pure async, or greenfield projects where you don't need Django's ecosystem. But if you already have Django — or you love Django's "batteries" — leaving hurts.
What if you could have FastAPI's ergonomics inside Django world? Enter Django Ninja.
4. Django Ninja: What It Is and Why It Matters
Django Ninja was created around 2020 — explicitly inspired by FastAPI but built for Django, not against it.
The core idea is to keep Django's ORM, admin, auth, and ecosystem. Replace only the API layer with something modern.
You have Pydantic validation in APIs, type hints everywhere, native async support, automatic OpenAPI docs and other modern features.
Key distinction I missed for too long: Django Ninja alone is not a REST framework. It's a modern HTTP layer. You add REST conventions on top if you want them (more on that in next sections).
Now let me explain why each feature matters in practice.
5. Let's Dive Deeper Into Key Features
5.1 Framework Speed (Request/Overhead)
Django Ninja isn't as fast as FastAPI, but it's meaningfully faster than DRF — and for most Django projects, that's the comparison that matters. DRF's request/response cycle passes through parsers, renderers, and content negotiators on every request, and its serializers do heavy runtime introspection in pure Python. Ninja skips most of that by default, and hands validation to Pydantic v2, whose core is written in Rust. Your database is still your real bottleneck, but lower validation overhead means better latency and a framework that doesn't fight you when you need async.
- DRF's serializers do a lot of work — field introspection, validation, object transformation
- DRF's request/response cycle passes through multiple layers (parsers, renderers, negotiators)
- Ninja does less by default — you opt into complexity
5.2 Pydantic vs. DRF Serializers: Speed + Syntax
Speed first:
Pydantic v2's validation core is written in Rust. DRF serializers are pure Python with heavy use of getattr, hasattr, and runtime introspection. The result is better performance for validation, which is even higher for complex nested validation.
This matters when you're validating arrays of objects or request bodies with 20+ fields.
And the syntax: the real win for developer happiness. DRF serializers are much more verbose compared to Pydantic scheams.
What I actually care about: Less code. Fewer files to jump between. Validation that lives with the schema. Autocomplete everywhere because Pydantic uses standard Python types. It is much easier to get how Pydantic works compared to DRF serializers and steep leanirng curve of this tool.
5.3 Where Django Ninja Sits on the Abstraction Spectrum
Think of it as a an spectrum: plain Django with JsonResponse at one end, full DRF with ViewSets and routers at the other, and Django + Ninja sitting cleanly in the middle. Most APIs don't actually need a full REST framework — they need validation, docs, and async. That's Ninja on its own, with no Meta classes, no serializer/view split, no router ceremony. The mistake I made for years was reaching for DRF by default and carrying all that weight before knowing if I needed it.
I used to reach for DRF by default. Now I ask: "Do I actually need ViewSets? Browsable API? Hyperlinked relationships?" 80% of the time, the answer is no.
5.4 Ninja + CRUD / Extra: When You Want RESTful Convenience (This Is the Real DRF Alternative)
If you genuinely want DRF-style RESTful conventions (ModelViewSet, automatic CRUD, router registration), don't build it yourself. Use these add-ons:
Django Ninja CRUD
-
ModelViewSet-like functionality on top of Ninja - Auto-generates endpoints:
GET /items,POST /items,GET /items/{id},PUT /items/{id},DELETE /items/{id} - Pydantic schemas auto-generated from your Django models
- Permission control per action (similar to DRF's
permission_classes)
Django Ninja Extra (My Preference)
- Class-based controllers
- Service layer injection — similar to FastAPI's dependency injection, but inside Django
- Separating API handling codes from business logic using service based controllers and dependency injection
- Advanced pagination, filtering, throttling out of the box
- OpenAPI extended features
- What DRF doesn't have: Native async, service-level DI, cleaner separation between HTTP layer and business logic
In DRF, business logic tends to bleed into views — one database call becomes a caching layer becomes an external API call, and suddenly your view is untestable. Ninja Extra's DI lets you put that logic in a service class and inject it into your controller, so the controller handles HTTP and nothing else. In tests you swap the service for a mock; in production you swap it for one that hits a cache first — the controller never changes. It's the clean separation DRF gestures toward but never quite delivers.
So the real comparison is: DRF vs. Ninja CRUD/Extra for RESTful conventions. Vanilla Ninja is a different (often better) choice for non-RESTful APIs.
6. Progressive Adoption: Ninja's Undervalued Superpower
Most "better" tools force a choice: rewrite everything or stay stuck.
Django Ninja gives you a third path: coexistence and gradual migration.
Real Example From My Last Project
We had a 30,000-line DRF codebase. Full rewrite? Impossible. Here's what we did in a large project:
1) Install Ninja alongside DRF (no breaking changes):
# urls.py
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('old_drf_urls')), # Existing DRF endpoints
path('ninja/api/', include('myapp.ninja_router')), # New Ninja based endpoints with a ninja/ prefix on paths
]
2) All new features written in Ninja:
- Same authentication (
request.userworks) - Same models (Django ORM unchanged)
- Same middleware (CORS, security, sessions — all work)
- Same testing tools (
django.test.Client,force_login)
Result: Zero friction. The team learned Ninja on real tasks, not tutorials.
3) Gradually migrate high-traffic DRF endpoints:
- Pick one endpoint (e.g.,
GET /api/v1/users/profile) - Write Ninja version with identical response structure
- Deploy both versions
- Update client application to use Ninja URL
- Remove DRF version after 2 weeks of stability
End result after 2 months: 80% of traffic served by Ninja, DRF endpoints still running but deprecated. No downtime. No big bang. No anxious weekends debugging a full rewrite.
This Works So Seamlessly, you don't need to migrate ORM, migrations, auth handlers, tests or admin panel. You also don't need to touch your deployment plan!
Ninja is not a migration — it's an upgrade path that respects your existing investment.
7. Conclusion: My 2026 Rule of Thumb
After two years of using Ninja in production, here's my honest decision table:
| Scenario | My Choice |
|---|---|
| New Django project, simple API (most cases) | Django Ninja (vanilla) |
| New Django project, CRUD-heavy (admin panel, internal tool) | Django Ninja + CRUD |
| New Django project, complex REST + service layer | Django Ninja Extra |
| Existing DRF project, adding new endpoints | Add Ninja alongside, don't touch old code |
| Existing DRF project with budget to modernize | Incremental migration to Ninja (endpoint by endpoint) |
| Team deeply tied to DRF patterns, no willingness to change | Stay with DRF — tooling isn't worth team friction |
| Non-Django project (microservice, pure async, no admin needed) | FastAPI or Litestar |
| Legacy DRF project with HATEOAS or browsable API requirements | DRF (Ninja doesn't have these by default) |
The Bottom Line
Django Ninja respects Django. It doesn't ask you to leave the Dajngo ecosystem. It just asks you to write better APIs with less frustration.
And the progressive adoption path means you can start today — on one endpoint — without any risk.
I'm not saying drop DRF from your projects. It's a really wonderful and robust framework. I'm saying: next time you start a new endpoint, try Ninja. See how it feels.
Most of my colleagues didn't want to switch. Now they refuse to go back.
Have you tried Django Ninja? Still on DRF? Made the jump to FastAPI and never looked back? Let me know in the comments — I genuinely enjoy hearing how other teams are solving these same problems.
And if you found this useful, consider sharing it with someone who's still writing ModelSerializer and wondering if there's a better way.
Top comments (0)