Introducing Django Ninja

This video features Vitaliy Kucheryaviy at DjangoCon Europe 2022 in Porto, Portugal.

Introducing Django Ninja
0:20:47
Published October 14, 2022
3,882 views

Introducing Django Ninja by Vitaliy Kucheryaviy

Django Ninja is the quickest way to build REST APIs, that will be fast, secure and documented automatically.

Summary

Django Ninja is a small Django library for building REST APIs with Python type annotations. Vitaliy Kucheryaviy explains how annotations, validated by Pydantic, can define request parameters and JSON bodies, enforce types, generate OpenAPI documentation, and produce useful client tooling without manual serialization or error handling. He demonstrates schemas for input and output, query parameters, headers, cookies, file uploads, nested database objects, pagination, and model-based schemas. He also shows how routers split larger APIs into reusable application modules, how sync and async endpoints can coexist, and argues that Django Ninja offers a concise, performant, production-ready alternative for Django API development.

Key takeaways

  • Django Ninja uses Python type annotations and Pydantic validation to enforce API inputs and generate useful error responses.
  • Schemas handle complex JSON payloads, type conversion, filtering of unwanted fields, and structured responses without manual serialization.
  • The library supports query parameters, headers, cookies, file uploads, nested model data, pagination, and automatic OpenAPI documentation.
  • Model schemas, routers, and multiple independent API instances help keep growing Django projects maintainable.
  • Django Ninja supports both synchronous and asynchronous views and can provide faster parsing and validation than Django REST Framework in some tests.

Summarised automatically from the transcript.

Chapters

  1. 0:04 Introduction to Django Ninja The speaker introduces Django Ninja, its FastAPI-inspired design, and its use of Python type annotations with Django.
  2. 1:38 Python Type Annotations An overview of annotations as developer hints and how they describe function arguments and return values.
  3. 3:14 Building a First API A minimal Django Ninja endpoint demonstrates routing, parameters, interactive documentation, and validation.
  4. 7:02 Request Schemas and JSON Payloads Schemas define complex request bodies, convert input types, and validate incoming blog post data.
  5. 10:16 OpenAPI Documentation The talk covers filtering unwanted fields and generating OpenAPI specifications, Swagger UI, and client code.
  6. 10:56 Performance and Async Views Django Ninja’s validation performance and its ability to combine synchronous and asynchronous views are discussed.
  7. 11:43 Query Parameters and File Uploads The speaker shows how to handle query filters, cookies, headers, and uploaded files.
  8. 13:17 Response Schemas and Pagination Response schemas serialize database objects, support nested relations, and add pagination to endpoints.
  9. 15:37 Model Schemas Model schemas automatically connect Django model fields to API schemas as models evolve.
  10. 16:24 Routers and API Organization Routers split endpoints across applications while supporting reusable, independent, and versioned APIs.
  11. 18:46 Project Status and Community The speaker reviews Django Ninja’s maturity, adoption, contributors, and ways to support the project.

Transcript

2,761 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:04

Hello, uh welcome to Introduction to Django Ninja My name is Vitaly Kuchirawi and today I'm gonna show you what Django Ninja is and why you should use it. So I work as a software developer for almost uh 20 years and most of the time I use Python and major part of data usually somewhere near Django code You probably noticed that I'm not at the stage at the moment. Well, that's because Russia invaded my country and I'm personally from City Kharky. which is shelled almost every day and almost every day we get casualties. But I would like to thank Jenga Khan Europe team

0:50

and everyone who listens now for this amazing opportunity to present my talk I really appreciate it. Thank you. So what is Django Ninja? So it's a relatively small library on top of Django that uses type annotations to create REST APIs. This project is heavily inspired by a framework called FASTAPI created by Sebastian Ramirez. So the secret source here is pretty simple. We take type annotations for describing APIs. These annotations are validated using Pydentic, and all this runs and integrates deeply with Django framework. Let's quickly jump into type hints or also known as type annotations.

1:38

So let's look at this function Right, so yeah, there is two arguments and the result is the plus operation between them. Uh and we cannot really know uh what are uh the a and b here. Are those numbers, are those strings, or maybe these are just some kind of remote uh data frames that happen to implement the plus operation. Uh so to help us here we can add um annotations yeah they are defined with this semicolon and the type of this argument and the result and here we can now understand yeah that the arguments are two numbers And the result of this operation will be also a number. And if you run it in interpreter, yeah, like so far so good, we added two numbers, we got the result.

2:28

But if we run a past there are not numbers but actually strings, yeah, there is no error, there is no warnings, yeah we got the result, but it's And again it's the result is not a number, it's a string. So as you can see the type annotations, they are not involved in route time at all These are just hints that can be used by linters or by editors. So for example in VS Code if I mark one of the arguments as int and other as string, you can see that now I got the warning that the plus operation is not allowed and it will eventually throw an error. So as you can see

3:14

it works on a lintel level but doesn't really work on a interpreter level So as you can see annotations are just hints and but on the other hand these hints we can use to create APIs. And yeah, let's do it. Let's create our first API. So first of all, install Django Ninja with Spip like so Then in some new module import from Ninja Ninja API class. And then we need just three lines. So first of all we initialize our API instance Then we use decorator.

3:59

In this case we use api. get to implement the get method. And inside we pass the path for that operation. In our case it's/add And finally we pass the request to our function. This is just the common Django approach, like all view functions, actually first argument request As you can see it's not even needed to mark a request with type annotations, but if you want to you always can. The race stays the same, a and b will be integers and the result will be the plus between A and B. And finally just put this API. urls

4:45

to URL patterns and that's it. You already have an ready API. And all you have to do now is to go to slash API slash docs. And there you will see the ready to use graphical interface and both documentation uh for the api which you just created. So in our case you see we have here the method slash API slash add that accepts two uh arguments a and b they will be passed in uh uh get parameters And let's try and execute it. And there you go, we get our results. So A and B are passed as

5:30

get parameters and we got our result. But unlike before our type annotations are now enforced. So if we pass, for example, not a number to our b argument. We will get a HTTP error that our B argument is not a valid integer number So basically we have now a guarantee that whatever we uh define in our functions will be always guaranteed and enforced And we don't have to worry about serialization, validation, and displaying the error messages back to the clients. And of course we can return any type of results.

6:16

So before we were outputting just a number, but we can actually return any structure that is convertible to JSON. It can be dict, it can be list, it can be any object that can be converted to JSON. And as well, like before, our arguments were passed as uh get arguments, but If we use mustache brackets in our path, we can mark them to be passed in path arguments. So here if we query the same URL with slash add slash three slash four, yeah we get our result uh as as value number seven. What other methods we can do?

7:02

It's actually pretty straightforward. So to send a post request, we use . api. post to put v write. put, delete and so on. So yeah, pretty straightforward. All popular HTTP methods are there and you can even use your own or combinations of multiple methods. To define your APIs. But where it mainly shines is when it comes to more complex JSON payloads that are submitted by clients. And the main horse here is a class called Schema. So let's create a new example where we will be creating

7:49

blog posts. with payloads. So all we have to do now is to define a new class that is inherited from schema And we can define there like as many as possible as complex as possible arguments. So it can be ins, dates, list of strings. These are just standard Python, there is nothing import here, there is nothing new. You basically define um uh with annotations. What you expect and the framework guarantees that you will get your results like you defined. And now all you have to do is in your post endpoint add an arguments uh which have annotation new post

8:35

and yeah there's a helper method dict that uh that can uh convert your object into a dictionary and yeah let's let's see how it works. Um so this is our uh request body example And what you note here is uh for example year we pass as a string and a timestamp we also pass as string, but uh as as not before we expect their uh year as a number and timestamp as a date. So now if we execute this request , this is what we get as a result. So yeah, you see the year is correctly converted to a number. And timestamp is also converted to a number, but yeah, it's actually in JSON

9:25

we only have uh dates, but uh yeah, believe me it's it's actually a date. But what's really cool about the way we use annotation is that when we use them, the editor can actually know what argument at this and as you can see here our timestamp is a date uh parameter and once we type a dot we can already use autocomplete to find uh the method that we need or the uh attribute that we need and yeah we never need to make any typos or mistakes so as you can see here vs code even suggests that yeah we made a typo here and we should better better uh to

10:10

change it so the more you the more type annotations you use the less um errors are in your code And schemes are also can be used to filter out unwanted data, like here if we pass some extra suspicious attribute, it will not be present in our validated payload By the way, Ninja automatically generates OpenAPI spec for all defined endpoints, which means you can use a lot of third-party tools like Swagger UI that I already showed. or redox that also comes as an option. Or you can even generate client-side code that will be automatically up to date with the changes that you make on the server side

10:56

And what about performance, right? So there's probably a big penalty for all this fancy validation. Well, actually no, and the opposite. Well, according to some tests in comparing with Jenga REST framework, ninja schemes can be sometimes like up to two times faster in data parsing and validation. And also you can use async views and more importantly you can mix sync and async views in one project So endpoints that are weighted like a lot of I. O. can run in one async worker and the CPU heavy task can run in multiple sync workers

11:43

Now uh in previous example we used schema in request body, but uh sometimes we sometimes we can have a ton of arguments in git in get parameters like tens of optional filters that are passed in URLs. Like in this example, we can define our filters as queryset lookups. And pass it as a function argument. And the only extra here we need to tell Ninja that it should be taken from query instead of body. To that, we can import this query marker, which will tell to get the data from git parameters instead of the body.

12:30

And yeah here to skip parameters that were not provided, you can use this exclude unset argument And yeah, finally just send all the lookups into query set filter. And that's it And yeah, the same way we also can access cookies and header values. You can define them like so. And as well get all the validation you define. Working with files is as easy. You just have to set your argument with a file marker. And a resulting value will be a standard Django file object with all the necessary attributes. Well, if you need a list of files, it is as easy.

13:17

Just set this argument with a list container. Now uh responses, right? So sometimes outputting nice results are as hard as parsing the data. Let's take an example the following database model. So yeah, this is a blog post with a bunch of fields. And now we need to output list of these posts. All we have to do is define a post schema with the same attributes as the fields And yeah, we just set the same types annotation that matches the database fields. And we set this class as a response attribute to our endpoint. Now if we check the interactive docs, yeah it

14:04

defines all the results correctly with all the attributes and when we execute it uh we are getting all the items in a way we define them. Oh, and did you notice like our endpoint just returned the query set? Yeah, there is no need to convert to any other intermediate dictionaries or lists or anything like that. Just one line of code. And also it is as easy work with nested objects. So if let's say our blog post have a foreign key to some category model , All we need to do is define category schema and just use category annotation and it will be automatically queried from the database and serialized.

14:50

And one of the simplest features is pagination. All you have to do is add this paginate decorator. By default it is limit offset paginator. If we open our interactive docs, yeah, we now see uh a pagination options And when we execute it, yeah, the results we have uh yeah, so this is our current page of items and the total number of results Next is schemes and models. Alright, so creating schemes for models is nice and intuitive, right? But What if you have like tens of fields in a model and these fields are constantly changing during development? Well, we have a special schema called model

15:37

schema. With model schema you can just configure the model and the fields that you need to sync to the schema. Well basically it's the same as Django model forms. Alright, and now whenever you change any fields in your model, the those changes will be automatically applied to corresponding schema. So every application tends to grow and yeah once you have more than five endpoints it's already a good time to start splitting your project into some logical pieces For that matter Ninja comes with a road structure. Let's take an example. So here is like a tree structure of some small project

16:24

Yeah, so there is uh um managed by project and there are three applications, some events, news and blocks. And each of these applications is supposed to have some uh API calls, yeah, at least five to ten uh uh endpoints each. So in this case, what we will do is we will create for each application a new module api. py. And inside this module, we import a router from Ninja. A router is basically a It's having the same interface as the Ninja API class. Yeah, it has all those get, posts, and all the rest

17:12

of functionality. So what you do is basically you uh inside each API module inside application you define as usual your uh endpoints And once you're done in inside your main module where you create your main ninja API instance You can include those rows from different modules using the add router method And yeah, in this case, like all the uh endpoints that were defined in uh events router. will be included to main

17:58

API and all the uh path will start with slash events. The same for news, all the news endpoints will start with slash news and same for blocks. So this is the way you can easily split your application into multiple uh independent modules and include them and uh even reuse if needed. Also keep in mind that Ninja API instances are also pretty independent. So you can for example have some public API in one Django project and in the same project you can have some private API And yeah, you can even have multiple versions of the same API and they will be all independent and will have their own

18:46

URLs and routes. I started this project about two years ago, basically as some proof of concept, well with just 400 lines of code. And as of today, please consider it a production ready library. It is 100% covered with tests, it has constant code reviews, bug fixes. and it is already used more by more than 10 companies, the names I know of, and I know it's a lot more are for sure. So yeah if you use Django Ninja, yeah please please drop me a line. And also, yeah, as of today , as I see on stats, the on a PyPy the

19:31

package has more than 50,000 downloads per month, which to me sounds like really crazy. And also I would like to take this opportunity to thank all the contributors who helped with the project. Yeah, I never thought that the open source project you spend only like one percent actually writing the code and yeah the the rest is way way more work and yeah if you have any ideas or PRs yeah please don't hesitate thank you And the easiest way you can help this project is to go and hit that star button on the GitHub page. Because yeah this will bring it to explore page

20:16

then then more people will come and more people again will click the start button and yeah they they they will will keep rolling Alright, thank you for having me and if it's technically possible, I hope I will answer your questions. Thank you.

Questions this talk answers

What is Django Ninja, and why should I use it?

Django Ninja is a small Django library for building REST APIs with Python type annotations. It uses those annotations for validation, serialization, and documentation while integrating closely with Django.

Discussed at 0:50

How do I create a REST API with Django Ninja?

Install Django Ninja, create a `NinjaAPI` instance, add an HTTP-method decorator such as `api.get`, define the endpoint function, and include the API URLs in Django’s URL configuration. Django Ninja then provides interactive documentation at `/api/docs`.

Discussed at 3:14

How does Django Ninja validate API parameters and return errors?

Type annotations are enforced for API inputs, so invalid values such as a non-integer integer parameter produce an HTTP validation error. Django Ninja handles validation, serialization, and formatting the error response automatically.

Discussed at 5:30

How do I define and validate a JSON request body in Django Ninja?

Create a class derived from `Schema`, annotate its fields with the expected Python types, and use that schema as the endpoint argument. Django Ninja converts compatible input values, rejects or filters unwanted data, and lets the handler use the validated object.

Discussed at 7:49

Does Django Ninja generate OpenAPI documentation and client code?

Yes. It automatically generates an OpenAPI specification for the defined endpoints, which can power tools such as Swagger UI and Redoc, and can also be used to generate client-side code.

Discussed at 10:10

Is Django Ninja fast, and can it use asynchronous views?

The talk says Django Ninja schema parsing and validation can be up to twice as fast as Django REST Framework in some tests. It supports async views and allows synchronous and asynchronous endpoints to coexist in the same project.

Discussed at 10:56

How do I handle query parameters, headers, cookies, and file uploads in Django Ninja?

Use the appropriate markers to tell Django Ninja whether values come from query parameters, headers, or cookies; validated query values can be passed into Django queryset filters. File parameters use a file marker and produce standard Django file objects, including support for lists of files.

Discussed at 11:43

How do I serialize Django model results and add pagination in Django Ninja?

Define a response schema matching the model fields and assign it to the endpoint; Django Ninja can serialize a queryset directly, including nested related objects. Add the pagination decorator to expose limit/offset pagination and return the current items along with the total count.

Discussed at 13:17

How can I keep Django Ninja schemas synchronized with model fields?

Use `ModelSchema` to configure the Django model and select the fields to expose. Changes to the model fields are then automatically reflected in the corresponding schema, similar to Django model forms.

Discussed at 15:37

How do I organize a large Django Ninja API into routers?

Create a router in each application, define that app’s endpoints there, and include the router in the main `NinjaAPI` with `add_router`. Prefixes such as `/events` or `/news` keep the modules separate, and multiple independent or versioned APIs can coexist.

Discussed at 16:24

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from DjangoCon Europe