django-api

Django API development for 2025. Covers Django Ninja (modern, async-first, type-safe) and Django REST Framework (mature, ecosystem-rich). Use when building REST APIs, choosing between frameworks, implementing authentication, permissions, filtering, pagination, or async endpoints.

16 stars

Best use case

django-api is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Django API development for 2025. Covers Django Ninja (modern, async-first, type-safe) and Django REST Framework (mature, ecosystem-rich). Use when building REST APIs, choosing between frameworks, implementing authentication, permissions, filtering, pagination, or async endpoints.

Teams using django-api should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/django-api/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/django-api/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/django-api/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How django-api Compares

Feature / Agentdjango-apiStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Django API development for 2025. Covers Django Ninja (modern, async-first, type-safe) and Django REST Framework (mature, ecosystem-rich). Use when building REST APIs, choosing between frameworks, implementing authentication, permissions, filtering, pagination, or async endpoints.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Django API Development (2025)

## Framework Choice

| Factor | Django Ninja | Django REST Framework |
|--------|--------------|----------------------|
| **Best for** | New projects, performance-critical, type-safety | Complex apps, mature ecosystem needs |
| **Validation** | Pydantic (type hints) | Serializers |
| **Async** | Native, first-class | Via `adrf` package |
| **Docs** | Auto-generated OpenAPI | Via drf-spectacular |
| **Learning curve** | Lower (FastAPI-like) | Steeper but well-documented |
| **Ecosystem** | Growing | Extensive third-party packages |

**Recommendation:** Start with Django Ninja for new projects. Use DRF when you need its ecosystem (complex permissions, nested routers, etc).

---

## Django Ninja (Recommended for 2025)

### Setup

```bash
pip install django-ninja
```

```python
# config/urls.py
from ninja import NinjaAPI

api = NinjaAPI(
    title="My API",
    version="1.0.0",
    docs_url="/docs",  # Swagger UI at /api/docs
)

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", api.urls),
]
```

### Schemas (Pydantic)

```python
# apps/blog/api/schemas.py
from ninja import Schema, ModelSchema
from datetime import datetime
from apps.blog.models import Article


class ArticleIn(Schema):
    title: str
    body: str
    tag_ids: list[int] = []


class ArticleOut(ModelSchema):
    author_name: str

    class Meta:
        model = Article
        fields = ["id", "title", "slug", "status", "created_at"]

    @staticmethod
    def resolve_author_name(obj: Article) -> str:
        return obj.author.username


class ArticleDetailOut(ArticleOut):
    body: str
    tags: list[str]

    @staticmethod
    def resolve_tags(obj: Article) -> list[str]:
        return [t.name for t in obj.tags.all()]
```

**Key patterns:**
- Use `Schema` for input, `ModelSchema` for output
- Type hints drive validation and docs
- Use `resolve_*` for computed fields

### Endpoints

```python
# apps/blog/api/views.py
from ninja import Router
from django.shortcuts import get_object_or_404
from apps.blog.models import Article
from .schemas import ArticleIn, ArticleOut, ArticleDetailOut

router = Router(tags=["articles"])


@router.get("/", response=list[ArticleOut])
def list_articles(request, status: str | None = None):
    qs = Article.objects.select_related("author")
    if status:
        qs = qs.filter(status=status)
    return qs


@router.get("/{slug}", response=ArticleDetailOut)
def get_article(request, slug: str):
    return get_object_or_404(
        Article.objects.select_related("author").prefetch_related("tags"),
        slug=slug,
    )


@router.post("/", response=ArticleOut)
def create_article(request, payload: ArticleIn):
    article = Article.objects.create(
        author=request.user,
        title=payload.title,
        body=payload.body,
    )
    if payload.tag_ids:
        article.tags.set(payload.tag_ids)
    return article
```

### Async Endpoints

```python
# apps/blog/api/views.py
from ninja import Router
from asgiref.sync import sync_to_async

router = Router()


@router.get("/articles/", response=list[ArticleOut])
async def list_articles(request):
    # Wrap ORM calls with sync_to_async
    qs = await sync_to_async(list)(
        Article.objects.select_related("author")[:20]
    )
    return qs


@router.get("/external-data/")
async def fetch_external(request):
    import httpx
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://api.example.com/data")
    return resp.json()
```

**When to use async:**
- External API calls (httpx, aiohttp)
- Multiple concurrent I/O operations
- Real-time / high-concurrency endpoints

**Note:** Django ORM is not fully async — wrap with `sync_to_async`.

### Authentication

```python
# apps/core/api/auth.py
from ninja.security import HttpBearer, APIKeyHeader
from django.contrib.auth.models import User


class AuthBearer(HttpBearer):
    def authenticate(self, request, token: str) -> User | None:
        # Validate JWT or token
        try:
            return User.objects.get(auth_token=token)
        except User.DoesNotExist:
            return None


class ApiKey(APIKeyHeader):
    param_name = "X-API-Key"

    def authenticate(self, request, key: str) -> User | None:
        try:
            return User.objects.get(api_key=key)
        except User.DoesNotExist:
            return None


# Usage
@router.get("/protected/", auth=AuthBearer())
def protected_endpoint(request):
    return {"user": request.auth.username}
```

### Wiring Routers

```python
# config/urls.py
from ninja import NinjaAPI
from apps.blog.api.views import router as blog_router
from apps.users.api.views import router as users_router

api = NinjaAPI()
api.add_router("/articles", blog_router)
api.add_router("/users", users_router)

urlpatterns = [
    path("api/v1/", api.urls),
]
```

---

## Django REST Framework

Use when you need the mature ecosystem or complex features.

### Setup

```python
# config/settings/base.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 20,
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
```

### Serializers

```python
# apps/blog/api/serializers.py
from rest_framework import serializers
from apps.blog.models import Article


class ArticleListSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField()

    class Meta:
        model = Article
        fields = ["id", "title", "slug", "author", "status", "created_at"]


class ArticleDetailSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField(read_only=True)
    tag_ids = serializers.PrimaryKeyRelatedField(
        queryset=Tag.objects.all(), many=True, write_only=True, source="tags"
    )

    class Meta:
        model = Article
        fields = ["id", "title", "slug", "body", "author", "tags", "tag_ids", "created_at"]
        read_only_fields = ["slug", "created_at"]
```

### ViewSets

```python
# apps/blog/api/views.py
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response


class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.select_related("author").prefetch_related("tags")
    lookup_field = "slug"

    def get_serializer_class(self):
        if self.action == "list":
            return ArticleListSerializer
        return ArticleDetailSerializer

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

    @action(detail=True, methods=["post"])
    def publish(self, request, slug=None):
        article = self.get_object()
        article.status = "published"
        article.save(update_fields=["status"])
        return Response({"status": "published"})
```

### Async DRF (via adrf)

```bash
pip install adrf
```

```python
from adrf.viewsets import ViewSet
from rest_framework.response import Response


class AsyncArticleViewSet(ViewSet):
    async def list(self, request):
        articles = await sync_to_async(list)(Article.objects.all()[:20])
        serializer = ArticleListSerializer(articles, many=True)
        return Response(serializer.data)
```

---

## Common Patterns (Both Frameworks)

### Filtering

```python
# Django Ninja
@router.get("/", response=list[ArticleOut])
def list_articles(
    request,
    status: str | None = None,
    author: str | None = None,
    created_after: date | None = None,
):
    qs = Article.objects.all()
    if status:
        qs = qs.filter(status=status)
    if author:
        qs = qs.filter(author__username=author)
    if created_after:
        qs = qs.filter(created_at__date__gte=created_after)
    return qs
```

```python
# DRF with django-filter
class ArticleFilter(django_filters.FilterSet):
    created_after = django_filters.DateFilter(field_name="created_at", lookup_expr="gte")

    class Meta:
        model = Article
        fields = ["status", "author__username"]
```

### Pagination

```python
# Django Ninja
from ninja.pagination import paginate, PageNumberPagination

@router.get("/", response=list[ArticleOut])
@paginate(PageNumberPagination, page_size=20)
def list_articles(request):
    return Article.objects.all()
```

### Error Handling

```python
# Django Ninja
from ninja.errors import HttpError

@router.get("/{id}")
def get_article(request, id: int):
    try:
        return Article.objects.get(id=id)
    except Article.DoesNotExist:
        raise HttpError(404, "Article not found")
```

---

## Testing

```python
# Django Ninja
from ninja.testing import TestClient
from config.urls import api

client = TestClient(api)


def test_list_articles():
    response = client.get("/articles/")
    assert response.status_code == 200


def test_create_article(authenticated_client):
    response = authenticated_client.post(
        "/articles/",
        json={"title": "Test", "body": "Content"},
    )
    assert response.status_code == 200
```

---

## Running with ASGI

For async support, use an ASGI server:

```bash
# Development
uvicorn config.asgi:application --reload

# Production
gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker -w 4
```

---

## Common Pitfalls

1. **Mixing sync/async incorrectly**: Use `sync_to_async` for ORM in async views. Don't call sync code directly.

2. **N+1 queries**: Always `select_related`/`prefetch_related` — both frameworks need this.

3. **Blocking in async views**: Use `httpx` (async) not `requests` (sync) for external calls.

4. **Over-engineering auth**: Start simple. Django Ninja's built-in `HttpBearer` or DRF's `IsAuthenticated` cover most cases.

5. **No OpenAPI docs**: Django Ninja auto-generates. For DRF, always add drf-spectacular.

Related Skills

django-pro

16
from diegosouzapw/awesome-omni-skill

Master Django 5.x with async views, DRF, Celery, and Django

Django Framework

16
from diegosouzapw/awesome-omni-skill

Build production-ready web applications with Django MVC, ORM, authentication, and REST APIs

django-expert

16
from diegosouzapw/awesome-omni-skill

Expert Django backend development guidance. Use when creating Django models, views, serializers, or APIs; debugging ORM queries or migrations; optimizing database performance; implementing authentication; writing tests; or working with Django REST Framework. Follows Django best practices and modern patterns.

django-developer

16
from diegosouzapw/awesome-omni-skill

Expert Django developer specializing in Async Views, Django Ninja (FastAPI-like), and HTMX patterns for modern full-stack apps.

api-scaffolding-django-pro

16
from diegosouzapw/awesome-omni-skill

Master Django 5.x with async views, DRF, Celery, and Django Channels. Build scalable web applications with proper architecture, testing, and deployment. Use PROACTIVELY for Django development, ORM optimization, or complex Django patterns. Use when: the request is Django or DRF focused (models, migrations, admin, serializers, viewsets, manage.py). Do not use when: the request is FastAPI/Pydantic/SQLAlchemy specific or centered on GraphQL schema-first design.

python-django-best-practices-cursorrules-prompt-fi

16
from diegosouzapw/awesome-omni-skill

Apply for python-django-best-practices-cursorrules-prompt-fi. --- description: Configurations for Django settings file with the list of dependencies and conventions. globs: **/settings.py

django_devops

16
from diegosouzapw/awesome-omni-skill

Enterprise Django 6 DevOps — containerization, CI/CD, environment management, database deployment, monitoring, and production readiness checklist.

django-project-setup

16
from diegosouzapw/awesome-omni-skill

Set up a new Django 6.0 project with modern tooling (uv, direnv, HTMX, OAuth, DRF, testing). Use when the user wants to create a Django project from scratch with production-ready configuration.

django-6-knowledge

16
from diegosouzapw/awesome-omni-skill

Provides knowledge about Django 6.0 features and implementation patterns. Use when working with Django projects, when the user mentions Django features, or when implementing Django functionality that may have changed in version 6.0.

agent-django-developer

16
from diegosouzapw/awesome-omni-skill

Expert Django developer mastering Django 4+ with modern Python practices. Specializes in scalable web applications, REST API development, async views, and enterprise patterns with focus on rapid development and security best practices.

bgo

10
from diegosouzapw/awesome-omni-skill

Automates the complete Blender build-go workflow, from building and packaging your extension/add-on to removing old versions, installing, enabling, and launching Blender for quick testing and iteration.

Coding & Development

e2e-test

16
from diegosouzapw/awesome-omni-skill

Run E2E test scenarios against running services. Use for happy path testing, unhappy flows, debugging, or when user says "otestuj", "proved test", "zkus flow".