API View¶
The APIView class provides a base for creating custom API endpoints outside of CRUD operations.
-
Organized Routing
Automatic tag grouping in OpenAPI docs
-
Auth Support
Per-view and per-endpoint authentication
-
Async First
Native async with automatic sync wrapping
Class Definition¶
| Attribute | Type | Description |
|---|---|---|
api |
NinjaAPI |
The NinjaAPI instance to register routes |
router_tag |
str |
Tag name for grouping endpoints in OpenAPI docs |
api_route_path |
str |
Base path for all routes in this view |
auth |
list \| None |
Authentication classes (optional) |
Creating Endpoints¶
Recommended: @api.view() with decorators¶
Use class method decorators to define endpoints. Decorators lazily bind instance methods to the router and automatically remove self from the OpenAPI signature while preserving type hints.
from ninja_aio import NinjaAIO
from ninja_aio.views import APIView
from ninja_aio.decorators import api_get, api_post
from ninja import Schema
api = NinjaAIO(title="My API")
class StatsSchema(Schema):
total: int
active: int
@api.view(prefix="/analytics", tags=["Analytics"])
class AnalyticsView(APIView):
@api_get("/dashboard", response=StatsSchema)
async def dashboard(self, request):
return {"total": 1000, "active": 750}
@api_post("/track")
async def track_event(self, request, event: str):
return {"tracked": event}
Available decorators (from ninja_aio.decorators):
| Decorator | HTTP Method |
|---|---|
@api_get(path, ...) |
GET |
@api_post(path, ...) |
POST |
@api_put(path, ...) |
PUT |
@api_patch(path, ...) |
PATCH |
@api_delete(path, ...) |
DELETE |
@api_options(path, ...) |
OPTIONS |
@api_head(path, ...) |
HEAD |
Decorator features
Decorators support per-endpoint auth, response, tags, summary, description, throttling, and OpenAPI extras. Sync methods are wrapped with sync_to_async automatically.
Alternative: views() method (legacy)¶
You can override views() to define endpoints imperatively. This approach still works but decorator-based endpoints are preferred.
class UserAPIView(APIView):
api = api_instance
router_tag = "Users"
api_route_path = "/users"
def views(self):
@self.router.get("/stats")
async def get_stats(request):
return {"total_users": 100}
@self.router.post("/bulk-create")
async def bulk_create(request, data: list[UserSchema]):
return {"created": len(data)}
class ProtectedAPIView(APIView):
api = api_instance
router_tag = "Protected"
api_route_path = "/protected"
auth = [JWTAuth()]
def views(self):
@self.router.get("/private", auth=self.auth)
async def private_data(request):
return {"user_id": request.auth.user_id}
@self.router.get("/public")
async def public_data(request):
return {"message": "This is public"}
Manual registration
When using @api.view(prefix="/path", tags=[...]), the router is mounted automatically. With the legacy approach, call add_views_to_route() to register endpoints manually.
When to Use APIView vs APIViewSet¶
| Use Case | APIView | APIViewSet |
|---|---|---|
| Custom analytics endpoints | ||
| Health checks, webhooks | ||
| Full CRUD for a model | ||
| Model with filtering + pagination | ||
| Mix of CRUD + custom endpoints | (add custom via decorators) |
Tip
Use APIView for non-CRUD endpoints. For CRUD operations, use APIViewSet which generates all endpoints automatically and still supports custom endpoints via decorators.
See Also¶
-
APIViewSet
Full CRUD operations with auto-generated endpoints
-
Decorators
View and operation decorators for custom endpoints
-
Authentication
JWT and custom auth configuration