NinjaAIORouter¶
NinjaAIORouter is a composable router that mirrors the NinjaAIO API surface. It lets you group related views and viewsets into a self-contained unit that can then be attached to the main API with a single call.
-
Logical Grouping
Bundle views and viewsets for a domain (e.g.
/v1,/admin) into one object -
Nested Routers
Each view/viewset gets its own sub-router, fully nested under the parent prefix
-
Two Attachment Styles
Use
api.add_router()or the@api.router()decorator — both are supported
Class Definition¶
NinjaAIORouter extends Django Ninja's Router, so every method available on a plain Router (.get(), .post(), add_router(), etc.) is available here too.
| Method | Description |
|---|---|
.view(prefix, tags) |
Register an APIView subclass as a nested sub-router |
.viewset(model, prefix, tags) |
Register an APIViewSet subclass as a nested sub-router |
Registering Views and ViewSets¶
The .view() and .viewset() decorators work identically to the ones on NinjaAIO:
from ninja_aio import NinjaAIO, NinjaAIORouter
from ninja_aio.views import APIView, APIViewSet
from ninja_aio.decorators import api_get
from ninja import Schema
api = NinjaAIO(title="My API")
class StatusSchema(Schema):
ok: bool
router = NinjaAIORouter()
@router.view(prefix="/health", tags=["Health"])
class HealthView(APIView):
@api_get("/check", response=StatusSchema)
async def check(self, request):
return {"ok": True}
@router.viewset(model=Article, prefix="/articles", tags=["Articles"])
class ArticleViewSet(APIViewSet):
schema_in = ArticleSchemaIn
schema_out = ArticleSchemaOut
schema_update = ArticleSchemaPatch
Attaching to NinjaAIO¶
Option 1 — api.add_router() (standard Django Ninja call)¶
Compatible with any NinjaAPI instance, not just NinjaAIO.
Option 2 — @api.router() decorator¶
@api.router("/v1")
class V1Router(NinjaAIORouter):
pass
@V1Router.view(prefix="/health", tags=["Health"])
class HealthView(APIView):
...
@V1Router.viewset(model=Article, prefix="/articles", tags=["Articles"])
class ArticleViewSet(APIViewSet):
...
The decorator instantiates the router class and mounts it on the API in one step. Additional keyword arguments are forwarded to api.add_router() (e.g. auth, tags).
Versioned API Example¶
A common pattern is using NinjaAIORouter to build versioned APIs:
from ninja_aio import NinjaAIO, NinjaAIORouter
from ninja_aio.views import APIView, APIViewSet
api = NinjaAIO(title="My API")
# --- v1 router ---
@api.router("/v1")
class V1Router(NinjaAIORouter):
pass
@V1Router.viewset(model=Article, prefix="/articles", tags=["v1 - Articles"])
class ArticleV1ViewSet(APIViewSet):
schema_in = ArticleSchemaIn
schema_out = ArticleSchemaOut
schema_update = ArticleSchemaPatch
# --- v2 router (extended schema) ---
@api.router("/v2")
class V2Router(NinjaAIORouter):
pass
@V2Router.viewset(model=Article, prefix="/articles", tags=["v2 - Articles"])
class ArticleV2ViewSet(APIViewSet):
schema_in = ArticleV2SchemaIn
schema_out = ArticleV2SchemaOut
schema_update = ArticleV2SchemaPatch
All routes end up correctly nested under their version prefix in the OpenAPI docs.
When to Use NinjaAIORouter¶
| Scenario | Recommendation |
|---|---|
| Small API with a handful of viewsets | Register directly on NinjaAIO with @api.viewset() |
Versioned API (/v1, /v2) |
Use one NinjaAIORouter per version |
| Domain-separated modules (auth, billing, catalog…) | Use one NinjaAIORouter per domain |
| Reusable router shipped as a library | Define the router in isolation, let consumers attach it |
See Also¶
-
APIView
Custom non-CRUD endpoints
-
APIViewSet
Full CRUD operations with auto-generated endpoints
-
Decorators
View and operation decorators