Getting started

Installation

Works for:

  • CPython 3.11+ or PyPy 3.11+

  • Django 4.2+

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

Extras for different features:

Important

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

We also recommend to always install django-stubs for typing the Django itself.

Note

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

LLMs support

Are you using AI for assisted coding? We 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 the LLMs.

Use cases we officially support:

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

  • AI-guided migrations for any API changes. We break something? We provide a prompt for you, so you can automatically upgrade to a newer version using an AI tool of your choice

We support several custom agent 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":"361c5d87-e4d8-4255-9e80-6013c9ab1c85"}

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"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/usercontroller/": {
      "post": {
        "deprecated": false,
        "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 choose 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 whatever models that we will use for our API

  2. We added two component parsers: one for request’s Body and one for Headers which will parse them into the typed models that we pass to these components as type parameters

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

  4. We also defined post API endpoint and returned a simple model response from it, it will be automatically transformed into 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' -H 'X-API-Consumer: my-api'
{"email":"user@wms.org","uid":"2db7687c-597c-4c2e-a6ee-f6eb9bca4eba"}

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

  • How to generate 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":"44bb0400-8889-4ea8-adb1-1d37393d4f43"}

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"
      }
    },
    "securitySchemes": {}
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/user/": {
      "post": {
        "deprecated": false,
        "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 right upper corner of the example, it shows up on hovering the code example. Paste it as example.py, install the django-modern-rest and run it with:

uv run example.py runserver

Your API is now live:

And then visit https://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 strict, security-first, battle-proven, highload-tested boilerplate for real apps of the modern age.

Next up

Core Concepts

Learn the fundamentals.

Core concepts
Configuration

Learn how to configure django-modern-rest.

Configuration