Validation

django-modern-rest has several layers of import-time and runtime validation. We try to do everything that we can during import-time, so it won’t affect your requests and responses.

However, we have to validate requests and responses during the runtime. Responses validation can be turned off in production for speed.

What do we validate and how?

Settings validation

We start with settings validation. We only validate settings once per application, we do it when the first Controller is created.

class dmr.validation.settings.SettingsValidator(*, serializer: type[BaseSerializer])[source]

Validates defined settings once.

__call__() None[source]

Collect and validate settings.

We also validate our own default values to be correct.

Endpoint validation

Next, when controller is being created, we run Endpoint validation.

Here we can detect all kinds of problems with how endpoints are defined:

HttpSpec validation

You can customize the strictness of HTTP Spec validation with overriding disabled HttpSpec options per-endpoint, per-controller, and globally.

Warning

We don’t recommend overriding any of these settings by default. It only makes sense to change, when implementing some old legacy API the “same” way as it used to be.

And only when you need this for a very specific reason.

Run result

$ curl http://127.0.0.1:8000/api/job/ -D - -X POST
HTTP/1.1 204 No Content
date: Tue, 14 Apr 2026 17:09:38 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 0
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
class dmr.validation.endpoint_metadata.EndpointMetadataBuilder(*, payload: ValidateEndpointPayload | ModifyEndpointPayload | None, controller_cls: type[Controller[BaseSerializer]], func: Callable[[...], Any], metadata_cls: type[EndpointMetadata], response_modification_cls: type[ResponseModification], component_parsers: list[tuple[ComponentParser, Any, tuple[Any, ...]]], type_annotations: dict[str, Any])[source]

Validate the metadata definition.

It is done during import-time only once, so it can be not blazing fast. It is better to be precise here than to be fast.

Here we only do structure and required validation. All semantic validation will be performed later on.

Metadata will NOT be considered ready after running this process.

class dmr.validation.endpoint_metadata.EndpointMetadataValidator(*, metadata: EndpointMetadata)[source]

Builds responses for the endpoint metadata.

Runs semantic validation.

Metadata will be considered ready after running this process.

Controller validation

The last step is the final Controller validation which has everything ready:

Here we validate:

  • That all Controller classes have unique methods

  • That all endpoints are either sync or async

  • All per-controller and per-endpoint error handling

class dmr.validation.controller.ControllerValidator[source]

Validates that controller is created correctly.

__call__(controller: type[Controller[BaseSerializer]]) bool | None[source]

Run the validation.

Response validation

The last step is to validate the response when returning it from the endpoint in runtime. We need this to make sure that API responses always match response schemas.

It can be turned off.

class dmr.validation.response.ResponseValidator(metadata: EndpointMetadata, serializer: type[BaseSerializer])[source]

Response validator.

Can validate responses that return raw data as well as real HttpResponse that are returned from endpoints.

validate_modification(endpoint: Endpoint, controller: Controller[BaseSerializer], structured: Any) ValidatedModification[source]

Validate structured data before dumping it to json.

validate_response(endpoint: Endpoint, controller: Controller[BaseSerializer], response: _ResponseT) _ResponseT[source]

Validate response based on provided schema.

final class dmr.validation.response.ValidatedModification(*, raw_data: Any, status_code: HTTPStatus, headers: dict[str, str], cookies: Mapping[str, NewCookie] | None, renderer: Renderer)[source]

Combines all validated data together.