API Development =============== This guide helps you choose the right API approach for your Django backend. The template ships with Django REST Framework by default, but GraphQL and django-ninja are viable alternatives depending on your requirements. Overview -------- Your API layer sits between HTTP requests and your service layer (see :doc:`service-layer-patterns`). The choice of API framework affects: - How you define request/response schemas - Authentication and permission patterns - OpenAPI documentation generation - Async support and performance characteristics The template's ``use_drf`` option enables Django REST Framework with drf-spectacular for OpenAPI generation. For frontend type safety with DRF, see :doc:`type-safe-api-integration`. Django REST Framework (Default) ------------------------------- DRF is the template's default choice because it's mature, has the largest ecosystem, and integrates directly with Django's authentication and ORM. Why DRF ^^^^^^^ - **Mature ecosystem**: Authentication backends, pagination, filtering, throttling all built-in or available as packages - **Browsable API**: Interactive documentation useful during development - **Serializers**: Declarative request/response validation with ORM integration - **drf-spectacular**: First-class OpenAPI 3.0 generation for type-safe frontend clients When to Use ^^^^^^^^^^^ DRF is the right choice for most projects, especially: - CRUD-heavy applications - Projects requiring extensive authentication options (OAuth, JWT, session, token) - Teams familiar with Django patterns - Applications where OpenAPI documentation is important Basic Pattern ^^^^^^^^^^^^^ With the services pattern, views become thin orchestration layers: .. code-block:: python # {project_slug}/tasks/api/serializers.py from rest_framework import serializers class TaskCreateInputSerializer(serializers.Serializer): title = serializers.CharField(max_length=200) description = serializers.CharField(required=False) class TaskSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField() description = serializers.CharField() status = serializers.CharField() created_at = serializers.DateTimeField() # {project_slug}/tasks/api/views.py from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView from {project_slug}.tasks.services import task_create from {project_slug}.tasks.selectors import task_list from .serializers import TaskCreateInputSerializer, TaskSerializer class TaskListCreateView(APIView): def get(self, request): tasks = task_list(fetched_by=request.user) return Response(TaskSerializer(tasks, many=True).data) def post(self, request): serializer = TaskCreateInputSerializer(data=request.data) serializer.is_valid(raise_exception=True) task = task_create( created_by=request.user, **serializer.validated_data, ) return Response(TaskSerializer(task).data, status=status.HTTP_201_CREATED) GraphQL ------- GraphQL offers a query language that lets clients request exactly the data they need. Consider it when your data relationships are complex or client needs vary significantly. When to Consider ^^^^^^^^^^^^^^^^ - **Complex data graphs**: Deeply nested relationships where REST would require multiple round-trips - **Diverse clients**: Mobile and web apps with different data requirements from the same backend - **API aggregation**: Unifying multiple data sources behind a single endpoint - **Rapid iteration**: Frontend teams need flexibility without backend changes for each new view Trade-offs ^^^^^^^^^^ +---------------------------+------------------------------------------+ | Advantage | Consideration | +===========================+==========================================+ | Flexible queries | HTTP caching is harder (POST requests) | +---------------------------+------------------------------------------+ | Single endpoint | N+1 query problems require careful | | | attention (use DataLoader) | +---------------------------+------------------------------------------+ | Strong typing | Learning curve for teams new to GraphQL | +---------------------------+------------------------------------------+ | Self-documenting schema | Tooling less mature than REST ecosystem | +---------------------------+------------------------------------------+ Library Options ^^^^^^^^^^^^^^^ **graphene-django** The established choice with Django ORM integration, relay support, and extensive documentation. Sync-only. - `graphene-django documentation `_ **strawberry-graphql** Modern, async-native library with type hints and dataclass-based schema definition. Better fit for async Django deployments. - `Strawberry documentation `_ Adding GraphQL to Your Project ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GraphQL is not included in the generated template. To add it: 1. Install your chosen library (``pip install graphene-django`` or ``pip install strawberry-graphql[django]``) 2. Add to ``INSTALLED_APPS`` 3. Define your schema in each module (e.g., ``{project_slug}/tasks/graphql/schema.py``) 4. Mount the GraphQL endpoint in ``config/urls.py`` .. note:: GraphQL support as a template option is on the roadmap. See :doc:`/6-about/roadmap`. django-ninja ------------ django-ninja brings FastAPI's developer experience to Django: type hints for validation, automatic OpenAPI generation, and native async support. When to Consider ^^^^^^^^^^^^^^^^ - **Async-first**: Your application heavily uses async views and you want ``use_async=y`` benefits throughout - **FastAPI familiarity**: Team has FastAPI experience and prefers that API style - **Pydantic validation**: You want Pydantic models for request/response validation - **Performance-critical**: Async endpoints for I/O-bound operations Strengths ^^^^^^^^^ - **Native async**: Built for async from the ground up - **Type-hint validation**: Request bodies, query params, and path params validated via type hints - **Built-in OpenAPI**: Automatic schema generation without additional packages - **Familiar syntax**: Similar to FastAPI, lower learning curve for those with that background Trade-offs ^^^^^^^^^^ +---------------------------+------------------------------------------+ | Advantage | Consideration | +===========================+==========================================+ | Async native | Smaller ecosystem than DRF | +---------------------------+------------------------------------------+ | Type-hint validation | Less Django ORM integration (no | | | ModelSerializer equivalent) | +---------------------------+------------------------------------------+ | FastAPI-like syntax | Fewer third-party extensions | +---------------------------+------------------------------------------+ | Built-in OpenAPI | Community and documentation smaller | +---------------------------+------------------------------------------+ Basic Pattern ^^^^^^^^^^^^^ .. code-block:: python # {project_slug}/tasks/api/routes.py from ninja import Router, Schema from typing import List from {project_slug}.tasks.services import task_create from {project_slug}.tasks.selectors import task_list router = Router() class TaskIn(Schema): title: str description: str = "" class TaskOut(Schema): id: int title: str description: str status: str @router.get("/", response=List[TaskOut]) def list_tasks(request): return task_list(fetched_by=request.user) @router.post("/", response=TaskOut) def create_task(request, payload: TaskIn): return task_create( created_by=request.user, title=payload.title, description=payload.description, ) Adding django-ninja to Your Project ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ django-ninja is not included in the generated template. To add it: 1. Install: ``pip install django-ninja`` 2. Create routers in each module 3. Mount the NinjaAPI in ``config/urls.py`` .. code-block:: python # config/urls.py from ninja import NinjaAPI api = NinjaAPI() api.add_router("/tasks/", "myproject.tasks.api.routes.router") urlpatterns = [ path("api/", api.urls), # ... ] .. note:: django-ninja support as a template option is on the roadmap. See :doc:`/6-about/roadmap`. Choosing the Right Approach --------------------------- Use this matrix to guide your decision: +---------------------+------------------+-------------------+------------------+ | Consideration | DRF | GraphQL | django-ninja | +=====================+==================+===================+==================+ | Async native | No (WSGI) | Strawberry: Yes | Yes | +---------------------+------------------+-------------------+------------------+ | Ecosystem size | Largest | Medium | Growing | +---------------------+------------------+-------------------+------------------+ | OpenAPI generation | Excellent | N/A (own schema) | Built-in | +---------------------+------------------+-------------------+------------------+ | Django ORM | Deep integration | Good | Manual mapping | | integration | | | | +---------------------+------------------+-------------------+------------------+ | Learning curve | Moderate | Steeper | Lower (FastAPI) | +---------------------+------------------+-------------------+------------------+ | Best for | Most apps | Complex graphs | Async-first | +---------------------+------------------+-------------------+------------------+ **Recommendation**: Start with DRF unless you have a specific reason not to. It's the safe default that works well for most Django applications. Consider alternatives when: - GraphQL: Your frontend team specifically requests it, or you have genuinely complex data graph requirements - django-ninja: You're building an async-first application and want that paradigm throughout your API layer See Also -------- - :doc:`type-safe-api-integration` — End-to-end type safety with DRF and React - :doc:`service-layer-patterns` — Where business logic belongs (not in views) - `Django REST Framework `_ — Official DRF documentation - `graphene-django `_ — GraphQL for Django - `Strawberry `_ — Async-native GraphQL - `django-ninja `_ — FastAPI-style APIs for Django