skills/sergio-bershadsky/ai/django-dev-ninja

django-dev-ninja

SKILL.md

Django Ninja API Development

Opinionated Django Ninja patterns with single-endpoint-per-file organization.

Core Principles

  1. One endpoint = one file - Each endpoint lives in its own file
  2. Logical grouping - Endpoints grouped in subpackages by domain
  3. Router per group - Each group has its own router
  4. Schemas in separate package - Pydantic models in schemas/
  5. Services for logic - Business logic in services, not endpoints

API Structure

myapp/
├── api/
│   ├── __init__.py           # Main NinjaAPI instance
│   ├── users/
│   │   ├── __init__.py       # Router: users_router
│   │   ├── list.py           # GET /users/
│   │   ├── detail.py         # GET /users/{id}
│   │   ├── create.py         # POST /users/
│   │   ├── update.py         # PUT /users/{id}
│   │   └── delete.py         # DELETE /users/{id}
│   ├── products/
│   │   ├── __init__.py
│   │   ├── list.py
│   │   ├── detail.py
│   │   └── search.py
│   └── auth/
│       ├── __init__.py
│       ├── login.py
│       ├── logout.py
│       └── refresh.py
└── schemas/
    ├── __init__.py
    ├── user.py               # UserIn, UserOut, UserPatch
    ├── product.py
    └── common.py             # Pagination, errors

Main API Setup

In api/__init__.py:

from ninja import NinjaAPI
from ninja.security import HttpBearer

from .users import router as users_router
from .products import router as products_router
from .auth import router as auth_router


class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        # Token validation logic
        from ..services.auth import AuthService
        return AuthService.validate_token(token)


api = NinjaAPI(
    title="My API",
    version="1.0.0",
    description="API documentation",
    auth=AuthBearer(),
)

# Register routers
api.add_router("/users", users_router, tags=["Users"])
api.add_router("/products", products_router, tags=["Products"])
api.add_router("/auth", auth_router, tags=["Authentication"], auth=None)

Router Setup

Each group has a router in __init__.py:

# api/users/__init__.py
from ninja import Router

from .list import router as list_router
from .detail import router as detail_router
from .create import router as create_router
from .update import router as update_router
from .delete import router as delete_router

router = Router()

# Merge endpoint routers
router.add_router("", list_router)
router.add_router("", detail_router)
router.add_router("", create_router)
router.add_router("", update_router)
router.add_router("", delete_router)

Endpoint File Template

Each endpoint in its own file:

# api/users/create.py
from ninja import Router
from django.http import HttpRequest

from ...schemas.user import UserIn, UserOut
from ...services.user import UserService

router = Router()


@router.post("/", response={201: UserOut})
def create_user(request: HttpRequest, payload: UserIn) -> UserOut:
    """Create a new user."""
    user = UserService.create(payload)
    return user

Schema Organization

Pydantic schemas in schemas/ package:

# schemas/user.py
from uuid import UUID
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field


class UserBase(BaseModel):
    """Shared user fields."""
    email: EmailStr
    name: str = Field(max_length=255)


class UserIn(UserBase):
    """Input schema for creating users."""
    password: str = Field(min_length=8)


class UserPatch(BaseModel):
    """Partial update schema."""
    email: EmailStr | None = None
    name: str | None = Field(None, max_length=255)


class UserOut(UserBase):
    """Output schema for users."""
    id: UUID
    is_active: bool
    created_at: datetime

    class Config:
        from_attributes = True

Common Patterns

Pagination

# schemas/common.py
from typing import Generic, TypeVar, List
from pydantic import BaseModel

T = TypeVar("T")


class PaginatedResponse(BaseModel, Generic[T]):
    items: List[T]
    total: int
    page: int
    per_page: int
    pages: int
# api/users/list.py
from ninja import Router, Query
from ...schemas.user import UserOut
from ...schemas.common import PaginatedResponse

router = Router()


@router.get("/", response=PaginatedResponse[UserOut])
def list_users(
    request,
    page: int = Query(1, ge=1),
    per_page: int = Query(20, ge=1, le=100),
):
    """List users with pagination."""
    from ...services.user import UserService
    return UserService.list_paginated(page, per_page)

Error Handling

# schemas/common.py
class ErrorResponse(BaseModel):
    detail: str
    code: str | None = None


class ValidationErrorResponse(BaseModel):
    detail: list[dict]
# api/users/detail.py
from ninja import Router
from django.http import Http404
from ...schemas.user import UserOut
from ...schemas.common import ErrorResponse

router = Router()


@router.get("/{user_id}", response={200: UserOut, 404: ErrorResponse})
def get_user(request, user_id: UUID):
    """Get user by ID."""
    from ...services.user import UserService

    user = UserService.get_by_id(user_id)
    if not user:
        return 404, {"detail": "User not found", "code": "USER_NOT_FOUND"}
    return user

File Upload

# api/files/upload.py
from ninja import Router, File, UploadedFile
from ...schemas.file import FileOut

router = Router()


@router.post("/upload", response=FileOut)
def upload_file(request, file: UploadedFile = File(...)):
    """Upload a file."""
    from ...services.file import FileService
    return FileService.save(file)

URL Configuration

Register API in urls.py:

# config/urls.py
from django.urls import path
from apps.myapp.api import api

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

Authentication Patterns

See references/endpoints.md for detailed authentication patterns including:

  • JWT authentication
  • API key authentication
  • Session authentication
  • Permission decorators

Additional Resources

Reference Files

  • references/endpoints.md - Detailed endpoint patterns, authentication, permissions
  • references/routers.md - Router organization, versioning, OpenAPI customization

Related Skills

  • django-dev - Core Django patterns and model organization
  • django-dev-test - Testing API endpoints with pytest
Weekly Installs
24
GitHub Stars
4
First Seen
Jan 25, 2026
Installed on
opencode23
gemini-cli23
codex23
github-copilot22
amp21
kimi-cli21