| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| 1.6.0 source code.tar.gz | 2026-03-12 | 3.7 MB | |
| 1.6.0 source code.zip | 2026-03-12 | 3.8 MB | |
| README.md | 2026-03-12 | 3.4 kB | |
| Totals: 3 Items | 7.5 MB | 0 | |
What's New
Idempotent Router(s)
Routers are now reusable and can be mounted to multiple APIs or multiple times within the same API. Decorators, auth, tags, and throttle settings are fully isolated between mounts.
:::python
router = Router(tags=["shared"])
@router.get("/items")
def list_items(request):
return [{"id": 1}]
# Mount same router to multiple APIs
api_v1 = NinjaAPI(urls_namespace="v1")
api_v1.add_router("/", router)
api_v2 = NinjaAPI(urls_namespace="v2")
api_v2.add_router("/", router) # !!! Before this was giving an error
Cursor Pagination
New CursorPagination class for stable pagination over frequently changing datasets. Uses base64-encoded cursor tokens instead of offsets, ensuring consistent results even when items are added or removed.
:::python
from ninja.pagination import paginate, CursorPagination
@api.get("/events", response=list[EventSchema])
@paginate(CursorPagination, ordering=("-created",), page_size=20)
def list_events(request):
return Event.objects.all()
Status Return
New Status class for explicitly returning HTTP status codes. Replaces the old tuple syntax (status_code, body) which is now deprecated.
:::python
from ninja import Status
@api.post("/login", response={200: Token, 401: Message})
def login(request, payload: Auth):
if not valid:
return Status(401, {"message": "Unauthorized"})
return Status(200, {"token": token})
Skip Re-validation
When returning a Pydantic model instance that already matches the response schema, Django Ninja now skips redundant validation and directly serializes — a nice performance boost.
:::python
@api.get("/user", response=UserOut)
def get_user(request):
return UserOut(id=1, name="John") # skips re-validation
Streaming Responses (JSONL & SSE)
First-class streaming support with automatic schema validation for each chunk. Supports both JSONL and Server-Sent Events formats.
:::python
from ninja.streaming import JSONL, SSE
@api.get("/items", response=JSONL[Item])
def stream_items(request):
for i in range(100):
yield {"name": f"item-{i}", "price": float(i)}
@api.get("/events", response=SSE[Item])
async def stream_events(request):
async for item in get_items():
yield item
Details
- Idempotent router(s) by @vitalik in https://github.com/vitalik/django-ninja/pull/1622
- Cursor pagination by @janrito in https://github.com/vitalik/django-ninja/pull/1657
- Status return, Skip revalidation by @vitalik in https://github.com/vitalik/django-ninja/pull/1684
- Streaming improvement by @vitalik in https://github.com/vitalik/django-ninja/pull/1685
- python 3.14 compatibility (namespace annotations) by @vitalik in https://github.com/vitalik/django-ninja/pull/1688
- Propose docs clarifications for view and operational decorators by @martinsvoboda in https://github.com/vitalik/django-ninja/pull/1689
- Optional Union fix by @Nuung in https://github.com/vitalik/django-ninja/pull/1690
New Contributors
- @janrito made their first contribution in https://github.com/vitalik/django-ninja/pull/1584
Full Changelog: https://github.com/vitalik/django-ninja/compare/v1.5.3...v1.6.0