Django
Mount pyRPC on a Django application using pyrpc_django.mount_django.
The Django adapter wires pyRPC into your URLconf with mount_django(urlpatterns). It uses Django's native async def views (Django 4.2+), so there is no bridge or wrapper needed.
Requirements
- Python 3.11+
- Django 4.2+
pyrpc-core[django]
1. Install
uv add pyrpc-core[django]
# or
pip install "pyrpc-core[django]"For CORS during local development:
uv add django-cors-headers
# or
pip install django-cors-headers2. Project scaffold
If you are starting a new project, follow the official Django tutorial to create the project structure:
django-admin startproject myproject
cd myproject
python manage.py startapp myappThe minimum file structure pyRPC needs:
myproject/
manage.py
myproject/
__init__.py
settings.py
urls.py
wsgi.py3. Settings
Add corsheaders and your app to INSTALLED_APPS. Put CorsMiddleware first in MIDDLEWARE -- this is required by django-cors-headers.
INSTALLED_APPS = [
"corsheaders",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"myproject",
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware", # must be first
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # React / Next.js
"http://localhost:5173", # Vue / Svelte (Vite)
]
CORS_ALLOW_CREDENTIALS = True4. Write procedures
Procedures are functions decorated with @rpc.query or @rpc.mutation. Django 4.2+ runs async def procedures as native async views with no bridge needed; plain def procedures work too.
from django.http import HttpResponse
from pyrpc_core import rpc
def index(request):
return HttpResponse("<h1>Django + pyRPC</h1>")
@rpc.query
async def greet(name: str = "World") -> dict:
return {"message": f"Hello, {name}!", "framework": "Django"}
@rpc.query
async def read_item(item_id: int, q: str = None) -> dict:
return {"item_id": item_id, "q": q}
@rpc.mutation
async def create_item(name: str, description: str = None) -> dict:
return {"name": name, "description": description, "created": True}5. Wire the URLs
Call mount_django(urlpatterns) in your URLconf. The import of views at the top is required -- it executes the @rpc decorators. Without it the procedures are never registered and /rpc returns an empty schema.
from django.contrib import admin
from django.urls import path
from pyrpc_django import mount_django
from . import views # required: triggers @rpc decorator execution
urlpatterns = [
path("admin/", admin.site.urls),
path("", views.index, name="index"),
]
mount_django(urlpatterns)mount_django appends two URL patterns to the list:
POST /rpc-- procedure dispatch (native async view)GET /rpc-- schema introspection (used bypyrpc devand the Python client)
6. Start the dev server
Run pyrpc dev from the directory containing manage.py to generate and keep TypeScript types in sync:
pyrpc dev --yes --framework django --module myproject.views --client ../clientFirst run: the wizard asks for your backend framework (Django), the path to manage.py, and a types module - the module whose import registers your @rpc procedures (usually the views.py that declares them, e.g. myproject.views; Django's settings and manage.py register nothing). This is written to pyrpc.json. Every run after reads it automatically.
pyrpc dev launches Django's own development server (manage.py runserver) for you, generates __pyrpc.ts in the client project, and regenerates it on every .py save. If you prefer to manage the server yourself, run it separately:
python manage.py runserverDjango runs the async RPC views natively -- no separate WSGI/ASGI bridge required.
7. Connect the frontend
import { createReactClient, httpBatchLink } from "@pyrpc/react"
import type { Types } from "@pyrpc/types"
export const api = createReactClient<Types>({
links: [
httpBatchLink({
url: "http://localhost:8000",
}),
],
})See React, Next.js, Vue, or Svelte for full frontend setup per framework.
How it works
@rpc.query/@rpc.mutationregister the function in the global procedure registry and tag it with a kind.mount_django(urlpatterns)appends the two RPC routes to your URLconf. The views arecsrf_exemptand return JSON directly.- The
from . import viewsline inurls.pyis what causes Python to execute the module and run the@rpcdecorators. This is the same mechanism Django uses for signals andAppConfig.ready(). async defprocedures use Django 4.2+'s native async view dispatch. There is noanyio.runwrapper.
Custom router
If you want to isolate procedures by app rather than using the global default router:
from pyrpc_core import Router
router = Router()
@router.query
async def list_items() -> list:
return []
@router.mutation
async def create_item(name: str) -> dict:
return {"name": name, "created": True}from myapp.procedures import router
from pyrpc_django import mount_django
urlpatterns = []
mount_django(urlpatterns, router=router)Full working examples
| Example | Frontend | Source |
|---|---|---|
| Django + React | React + TanStack Query | examples/django-react |
| Django + Next.js | Next.js App Router + RSC | examples/django-nextjs |
| Django + Vue | Vue 3 + TanStack Vue Query | examples/django-vue |
| Django + Svelte | Svelte + TanStack Svelte Query | examples/django-svelte |

pyRPC