Getting started#

Installation#

Works for:

  • CPython 3.11+ or PyPy 3.11+

  • Django 5.0+

uv add django-modern-rest

Extras for different serializers:

  • 'django-modern-rest[pydantic]' for pydantic support

  • 'django-modern-rest[attrs]' for attrs support

  • 'django-modern-rest[msgspec]' for msgspec support and the fastest json parsing in requests, jwt, testing, and OpenAPI

Extras for different features:

Important

We highly recommend always installing msgspec, even when using just pydantic for APIs. We use msgspec to parse json when it is available because it is the fastest library out there for this task.

We also recommend always installing django-stubs for typing Django itself.

Note

You don’t need to add 'dmr' to INSTALLED_APPS unless you want to serve static files for OpenAPI.

LLMs support#

Are you using AI for assisted coding? We’ve got you covered. Use these files for context to make sure that the LLM knows our framework:

We also support Context7 for up-to-date docs for LLMs.

Use cases we officially support:

  • Learning django-modern-rest with the help of DeepWiki

  • AI-guided migrations for any API changes. Did we break something? Every breaking release ships a migration prompt, so you can upgrade to a newer version using an AI tool of your choice

We ship several agent skills inside the package, install them into your project with a single command:

uvx library-skills

Showcase#

Let’s see the basics and learn how to use dmr in a single example:

We support msgspec.Struct via MsgspecSerializer.

Run result

$ curl http://127.0.0.1:8000/api/user/ -X POST -d '{"email": "email@example.com"}' -H 'Content-Type: application/json'
{"email":"email@example.com","uid":"eaec2882-4897-4afe-839c-dc51454235bc"}

OpenAPI Schema

Preview openapi.json
{
  "components": {
    "schemas": {
      "ErrorDetail": {
        "description": "Base schema for error details description.",
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "type": "array"
          },
          "msg": {
            "type": "string"
          },
          "type": {
            "type": "string"
          }
        },
        "required": [
          "msg"
        ],
        "title": "ErrorDetail",
        "type": "object"
      },
      "ErrorModel": {
        "description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            },
            "type": "array"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorModel",
        "type": "object"
      },
      "UserCreateModel": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "title": "UserCreateModel",
        "type": "object"
      },
      "UserModel": {
        "properties": {
          "email": {
            "type": "string"
          },
          "uid": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "email",
          "uid"
        ],
        "title": "UserModel",
        "type": "object"
      }
    }
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/usercontroller/": {
      "post": {
        "operationId": "postUserControllerApiUsercontroller",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserModel"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when request components cannot be parsed"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

Important

You can choose a serializer per controller, which will give you the freedom to pick the best serializer and model for the job. msgspec gives you more speed, while pydantic gives you more flexibility.

In this example:

  1. We defined regular pydantic, msgspec, or any other models we will use for our API

  2. We added a component parser for the request’s Body, which will parse it into the typed model that we pass to this component as a type parameter

  3. Next, we created a Controller class with PydanticSerializer or MsgspecSerializer to serialize input and output data for us

  4. We also defined a post API endpoint and returned a simple model response from it. It will be automatically transformed into a django.http.HttpResponse instance by django-modern-rest

Now, let’s add our controller to the list of URLs:

Run result

$ curl http://127.0.0.1:8000/api/user/ -X POST -d '{"email": "user@wms.org"}' -H 'Content-Type: application/json'
{"email":"user@wms.org","uid":"9e138f7d-2906-4c6c-923f-0f016d6cc272"}

Your first django-modern-rest API is ready. Next, you can learn:

  • How to generate an OpenAPI schema

  • How to handle errors

  • How to customize controllers and endpoints

Full example#

If you were ever told that Django is too big and complicated, that was misleading, to say the least.

Here’s a single-file application that looks pretty much the same as any other micro-framework, like FastAPI, Litestar, or Flask.

Run result

$ curl http://127.0.0.1:8000/api/user/ -X POST -d '{"email": "djangomodernrest@wemake.services"}' -H 'Content-Type: application/json'
{"email":"djangomodernrest@wemake.services","uid":"ef5ed1c1-876a-4447-bd47-326b9455ff95"}

OpenAPI Schema

Preview openapi.json
{
  "components": {
    "schemas": {
      "ErrorDetail": {
        "description": "Base schema for error details description.",
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "title": "Loc",
            "type": "array"
          },
          "msg": {
            "title": "Msg",
            "type": "string"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "msg"
        ],
        "title": "ErrorDetail",
        "type": "object"
      },
      "ErrorModel": {
        "description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "required": [
          "detail"
        ],
        "title": "ErrorModel",
        "type": "object"
      },
      "UserCreateModel": {
        "properties": {
          "email": {
            "title": "Email",
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "title": "UserCreateModel",
        "type": "object"
      },
      "UserResponseModel": {
        "properties": {
          "email": {
            "title": "Email",
            "type": "string"
          },
          "uid": {
            "format": "uuid",
            "title": "Uid",
            "type": "string"
          }
        },
        "required": [
          "email",
          "uid"
        ],
        "title": "UserResponseModel",
        "type": "object"
      }
    }
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/user/": {
      "post": {
        "operationId": "postUserControllerApiUser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponseModel"
                }
              }
            },
            "description": "Created"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when request components cannot be parsed"
          },
          "406": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when provided `Accept` header cannot be satisfied"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Raised when returned response does not match the response schema"
          }
        }
      }
    }
  }
}

You can copy it by clicking “Copy” in the upper-right corner of the example. The copy button appears when you hover over the code example. Paste it as example.py, install django-modern-rest, and run it with:

uv run python example.py runserver

Your API is now live:

Then visit http://localhost:8000/docs/swagger/ for the interactive docs.

Swagger view

That’s it, enjoy your new project!

But this is too simple for my use case!#

What is great about Django is that it scales. You can start with a single-file app and scale it up to a full-featured monolith with strict context boundaries, DDD, reusable apps, etc.

We recommend starting new big projects with https://github.com/wemake-services/wemake-django-template.

It is a strict, security-first, battle-proven, highload-tested boilerplate for real apps of the modern age.

Type checking#

django-modern-rest officially supports all major Python type checkers in strict mode (listed in order of recommendation):

Installing django-stubs is required for all of them.

Next up#

Core Concepts

Learn the fundamentals.

Core concepts
Configuration

Learn how to configure django-modern-rest.

Configuration