Lazy Ninja: Generate API Endpoints for Django¶
Lazy Ninja is a Django library that automates the generation of API endpoints with Django Ninja. It dynamically scans your Django models and creates Pydantic schemas for listing, detailing, creating, and updating records—all while allowing you to customize behavior via hook functions (controllers) and schema configurations.
By leveraging Django Ninja, Lazy Ninja benefits from automatic, interactive API documentation generated through OpenAPI (Swagger UI and ReDoc), giving developers an intuitive interface to quickly visualize and interact with API endpoints.
Async by Default: Lazy Ninja is designed to be asynchronous by default, leveraging Django's async capabilities for better performance. However, if needed, you can configure it to use synchronous operations by setting the
is_asyncparameter toFalse.
Key Features:
- Instant API Endpoints: Automatically generate API endpoints from Django models.
- Dynamic Schema Generation: Automatically create Pydantic models.
- Customizable Hooks: Add pre-processing, post-processing, or custom logic to routes by creating controllers for specific models.
- Smart Filtering/Sorting: Built-in support for filters like
field=valueorfield>value. - Auto Documentation: Interactive Swagger UI and ReDoc support.
- Performance Optimized: Lightweight with minimal overhead.
- Authentication (NEW): JWT-based authentication with stateless/stateful modes, refresh token rotation lifecycle hooks, and optional token revocation.
Installation¶
Requirements¶
- Python: 3.7 or higher
- Django: 3.1 or higher
- Django Ninja: 0.22 or higher
Note: While the library may work with older versions of Python or Django, these are the versions officially tested and recommended.
Install the package¶
Run one of the following commands to install the package:
For local development (recommended), use uv:
Quick Start¶
1. Create a Model¶
# models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
in_stock = models.BooleanField(default=True)
2. Configure the API¶
# api.py
from ninja import NinjaAPI
from lazy_ninja.builder import DynamicAPI
api = NinjaAPI(title="Store API")
auto_api = DynamicAPI(api, pagination_type="limit-offset")
auto_api.init()
3. Add URLs¶
# urls.py
from django.urls import path
from .api import api
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
]
4. Migrate Your Database¶
5. Run the Server¶
Instant API Endpoints¶
Your API is now live at http://localhost:8000/api with these endpoints:
| Method | URL | Action |
|---|---|---|
| GET | /api/products/ | List products |
| POST | /api/products/ | Create product |
| GET | /api/products/{id} | Get product |
| PATCH | /api/products/{id} | Update product |
| DELETE | /api/products/{id} | Delete product |
Interactive Documentation¶
Access auto-generated documentation through:
Swagger UI¶
http://localhost:8000/api/docs

CLI Usage¶
Lazy Ninja ships with a Command Line Interface (CLI) to speed up common tasks:
-
Scaffold a new Django project with Lazy Ninja preconfigured.
-
Generate OpenAPI clients/SDKs for multiple languages.
It removes boilerplate and keeps backend ↔ frontend integration consistent.
Overview¶
-
lazy-ninja init(aliasstartproject) — create a Django project scaffold (api.py,urls.pyready). -
lazy-ninja generate-client— generate OpenAPI clients/SDKs (TypeScript, Dart, Python, Java, Go, C#, Ruby, Swift, etc.). -
CLI auto-exports your schema from Django + Ninja (no server run needed), or you can provide
--schema.
Prerequisites¶
-
generate-clientneedsopenapi-generator-cli.-
Install with
uv add "lazy-ninja[standard]"(includes JDK viajdk4py). -
If you already have Java:
uv add "lazy-ninja[no-jdk]".
-
-
For
typescript-types: requires Node.js/npm (usesnpx openapi-typescript). -
Offline mode: pre-generate schema and use
--schema openapi.json.
init — Project scaffold¶
Creates a Django project, adds api.py + urls.py, and comments in settings.py showing how to enable the API.
generate-client — Client/SDK generation¶
lazy-ninja generate-client <language> \
--settings myproject.settings \
--api-module myproject.api \
--output ./clients/<target>
Options:
-
<language>— generator name (typescript-axios,python,dart, etc.). -
--schema— skip Django import, use a pre-generated OpenAPI JSON. -
--api-var— defaults toapi.
Examples:
# TypeScript axios client
lazy-ninja generate-client typescript-axios --settings myproject.settings --output ./clients/ts-axios
# Dart client
lazy-ninja generate-client dart --settings myproject.settings --output ./clients/dart
# From pre-generated schema
lazy-ninja generate-client python --schema ./openapi.json --output ./clients/python
Supported generators¶
-
typescript-types(vianpx openapi-typescript) -
typescript-axios,typescript-fetch -
dart,dart-dio -
python -
java,kotlin,go,csharp,ruby,swift5
(seeGENERATOR_CONFIGfor full list)
Benefits¶
-
Zero-boilerplate — scaffold project + API with one command.
-
Automatic schema export — generate clients without running the server.
-
Multi-target — TypeScript clients for frontend, SDKs for backend.
-
Offline ready — use
--schemafor CI/CD or locked-down environments.
Advanced Example¶
Custom Schema with Validation¶
# schemas.py
from pydantic import BaseModel, Field
class ProductCreateSchema(BaseModel):
name: str = Field(..., min_length=2, max_length=100)
price: float = Field(..., gt=0)
in_stock: bool = True
class ProductUpdateSchema(ProductCreate):
__annotations__ = {k: Optional[v] for k, v in ProductCreate.__annotations__.items()}
Configure Endpoints¶
# api.py
from .schemas import ProductCreateSchema, ProductUpdateSchema
custom_schemas = {
"Product": {
"create": ProductCreateSchema,
"update": ProductUpdateSchema
}
}
auto_api = DynamicAPI(api, custom_schemas=custom_schemas)
Using a Custom Controller¶
To further customize the behavior of your API, you can create a controller for specific models. Controllers allow you to add hooks such as before_create, before_update, or after_delete to modify the behavior of routes.
For example, here’s how to use a controller with the Product model:
# controllers/product.py
from django.utils.text import slugify
from lazy_ninja import BaseModelController, controller_for
@controller_for("Product")
class ProductController(BaseModelController):
@classmethod
def before_create(cls, request, payload, create_schema):
"""
Hook executed before creating a new Product.
It validates the 'name' field against forbidden words,
converts it to lowercase, and automatically generates a slug.
"""
forbidden_words = ["forbidden", "banned", "test"]
payload_data = payload.model_dump()
for word in forbidden_words:
if word in payload_data['name'].lower():
raise ValueError(f"Invalid name: contains forbidden word '{word}'")
# Generate a slug for the product name
payload_data['slug'] = slugify(payload_data['name'])
return create_schema(**payload_data)
- Create a folder named
controllersin your project/app directory (if it doesn't already exist). - Add your controller files inside this folder (e.g.,
product.py). - Use the
@controller_fordecorator to link the controller to theProductmodel.
This setup ensures that the before_create hook is called whenever a new Product is created, allowing you to validate or modify the payload before saving it to the database.
Advanced Configuration¶
Excluding apps or models¶
Lazy Ninja supports exclusion configuration. You can exclude entire apps or specific models by providing an exclude configuration.
For example:
from lazy_ninja.builder import DynamicAPI
# Exclude an entire app (e.g., Django's internal apps are excluded by default)
# and/or specific models.
exclude_config = {
"blog": True, # Exclude all models from this app
"store": { "Product", "Comment"} # Exclude specific models from this app
}
auto_api = DynamicAPI(api,exclude=exclude_config)
auto_api.init()
Authentication¶
Lazy Ninja ships with an optional JWT auth module. It supports stateless or stateful modes, configurable login identifiers, and lifecycle hooks.
Quick example:
from ninja import NinjaAPI
from lazy_ninja.auth import register_auth_routes
api = NinjaAPI()
register_auth_routes(api)
Read the Authentication Guide.
Query Features¶
Filtering¶
Simple filtering is supported, and the query string can be formatted in several ways:
-
"published=true"translates to filtering by thepublishedfield. -
"views>10"translates to filtering whereviewsis greater than 10. -
"title=test"uses a case-insensitiveicontainsfilter on thetitlefield.
Example filter parsing:
This filters products to show only those where in_stock is true and price is greater than 12.Sorting¶
Sorting is supported via query parameters. For example:
This sorts products first byprice in descending order (desc), then by name in ascending order (asc) for items with the same price.
Pagination¶
Built-in support for pagination, allowing you to efficiently navigate through large datasets by splitting results into manageable chunks. You can control pagination using query parameters in your API requests. Two strategies are supported: Limit-Offset (default) and Page Number.
Limit-Offset Pagination (Default)¶
This strategy lets you specify how many items to return (limit) and how many to skip (offset).
Example:
Page Number Pagination¶
This strategy uses page numbers to navigate results. You specify the page number, and the page size is configurable.
Example:
Returns the second page of products.Note: To set the page size (e.g., 10 items per page), define NINJA_PAGINATION_PER_PAGE in your Django settings.py. For example:
Configuring Pagination¶
By default, Lazy Ninja uses Limit-Offset pagination. To switch to Page Number pagination or customize the strategy, pass the pagination_type parameter when initializing DynamicAPI:
Alternatively, set NINJA_PAGINATION_CLASS in settings.py to override the default globally.
File Upload Support¶
Lazy Ninja supports handling file uploads for models with FileField and ImageField using multipart/form-data. This feature allows you to define which fields should use multipart/form-data and provides flexibility to handle mixed models where some routes use JSON while others use multipart/form-data.
How to Use File Upload Parameters¶
When initializing DynamicAPI, you can configure the following parameters:
file_fields: Specify which fields in a model should usemultipart/form-data.use_multipart: Explicitly define whethercreateandupdateoperations for specific models should usemultipart/form-data.auto_detect_files: Automatically detectFileFieldandImageFieldin models (default:True).auto_multipart: Automatically enablemultipart/form-datafor models with detected file fields (default:True).
Example Usage¶
from ninja import NinjaAPI
from lazy_ninja.builder import DynamicAPI
api = NinjaAPI()
auto_api = DynamicAPI(
api,
is_async=True,
file_fields={"Gallery": ["images"], "Product": ["pimages"]}, # Specify file fields
use_multipart={
"Product": {
"create": True, # Use multipart/form-data for creation
"update": True # Use multipart/form-data for updates
}
},
auto_multipart=False # Disable automatic multipart/form-data for detected file fields
)
auto_api.register_all_models()
In this example:
- The Gallery model will use multipart/form-data for the images field.
- The Product model will use multipart/form-data for the pimages field during create and update operations.
- Models without file fields will continue to use JSON by default.
By default,
auto_multipartis True and routes for models with file fields will usemultipart/form-dataautomatically. If you want to disable this behavior, set auto_multipart=False.
Custom Middleware for PUT/PATCH with Multipart¶
To handle PUT and PATCH requests with multipart/form-data, Lazy Ninja includes a custom middleware: ProcessPutPatchMiddleware. This middleware ensures that PUT and PATCH requests are processed correctly by temporarily converting them to POST for form data handling.
Why This Middleware is Needed¶
Django has a known limitation where PUT and PATCH requests with multipart/form-data are not processed correctly. While Django Ninja recently introduced updates to address this, certain scenarios still require a custom solution. This middleware resolves those issues and ensures compatibility with both synchronous and asynchronous request handlers.
How to Use the Middleware¶
Add the middleware to your Django project's MIDDLEWARE setting:
How It Works¶
- Converts
PUTandPATCHrequests withmultipart/form-datatoPOSTtemporarily for proper processing. - Restores the original HTTP method after processing the request.
Contributing & Feedback¶
Lazy Ninja is still evolving — contributions, suggestions, and feedback are more than welcome! Feel free to open issues, discuss ideas, or submit PRs.
License¶
This project is licensed under the terms of the MIT license.