Skip to content

Response renderers

The most common response type for a REST API is usually JSON. Django Ninja also has support for defining your own custom renderers, which gives you the flexibility to design your own media types.

Create a renderer

To create your own renderer, you need to inherit ninja.renderers.BaseRenderer and override the render method. Then you can pass an instance of your class to NinjaAPI as the renderer argument:

from ninja import NinjaAPI
from ninja.renderers import BaseRenderer


class MyRenderer(BaseRenderer):
    media_type = "text/plain"

    def render(self, request, data, *, response_status):
        return ... # your serialization here

api = NinjaAPI(renderer=MyRenderer())

The render method takes the following arguments:

  • request -> HttpRequest object
  • data -> object that needs to be serialized
  • response_status as an int -> the HTTP status code that will be returned to the client

You need also define the media_type attribute on the class to set the content-type header for the response.

ORJSON renderer example:

orjson is a fast, accurate JSON library for Python. It benchmarks as the fastest Python library for JSON and is more accurate than the standard json library or other third-party libraries. It also serializes dataclass, datetime, numpy, and UUID instances natively.

Here's an example renderer class that uses orjson:

import orjson
from ninja import NinjaAPI
from ninja.renderers import BaseRenderer


class ORJSONRenderer(BaseRenderer):
    media_type = "application/json"

    def render(self, request, data, *, response_status):
        return orjson.dumps(data)

api = NinjaAPI(renderer=ORJSONRenderer())

XML renderer example:

This is how you create a renderer that outputs all responses as XML:

from io import StringIO
from django.utils.encoding import force_str
from django.utils.xmlutils import SimplerXMLGenerator
from ninja import NinjaAPI
from ninja.renderers import BaseRenderer


class XMLRenderer(BaseRenderer):
    media_type = "text/xml"

    def render(self, request, data, *, response_status):
        stream = StringIO()
        xml = SimplerXMLGenerator(stream, "utf-8")
        xml.startDocument()
        xml.startElement("data", {})
        self._to_xml(xml, data)
        xml.endElement("data")
        xml.endDocument()
        return stream.getvalue()

    def _to_xml(self, xml, data):
        if isinstance(data, (list, tuple)):
            for item in data:
                xml.startElement("item", {})
                self._to_xml(xml, item)
                xml.endElement("item")

        elif isinstance(data, dict):
            for key, value in data.items():
                xml.startElement(key, {})
                self._to_xml(xml, value)
                xml.endElement(key)

        elif data is None:
            # Don't output any value
            pass

        else:
            xml.characters(force_str(data))


api = NinjaAPI(renderer=XMLRenderer())
(Copyright note: this code is basically copied from DRF-xml)