Part 1: Project setup & layout
One router per app, schemas from models, and task URLs nested under projects.
5 minute read
This tutorial builds one realistic API from start to finish: TaskFlow, a task tracker for teams. Users sign up, create projects, invite members with roles, and track tasks with assignees, labels, statuses, due dates, comments and file attachments. Each part adds a feature to the same codebase and shows the Django Ninja tools that make it work.
By the end of Part 8, TaskFlow has these endpoints:
| Method | URL | What it does |
|---|---|---|
POST |
/taskflow/api/auth/signup |
Create an account |
POST |
/taskflow/api/auth/token, /taskflow/api/auth/refresh |
Get and refresh JWT access tokens |
GET, POST |
/taskflow/api/projects/ |
List your projects, create one |
GET, PUT, DELETE |
/taskflow/api/projects/{project_id} |
Read, update and delete a project |
POST |
/taskflow/api/projects/{project_id}/labels |
Add a label to a project |
GET, POST |
/taskflow/api/projects/{project_id}/members |
List and add members |
GET, POST |
/taskflow/api/projects/{project_id}/tasks/ |
Filter, search, sort and paginate tasks, create one |
GET, PATCH |
/taskflow/api/projects/{project_id}/tasks/{task_id} |
Read a task, update part of it |
POST |
/taskflow/api/projects/{project_id}/tasks/{task_id}/status |
Move a task through its workflow |
GET, POST |
/taskflow/api/projects/{project_id}/tasks/{task_id}/comments |
List comments with a cursor, add one |
PUT, DELETE |
/taskflow/api/projects/{project_id}/tasks/{task_id}/comments/{comment_id} |
Edit or delete a comment |
GET, POST |
/taskflow/api/projects/{project_id}/tasks/{task_id}/attachments |
List attachments, upload a file |
DELETE |
/taskflow/api/projects/{project_id}/tasks/{task_id}/attachments/{attachment_id} |
Delete an attachment |
Every endpoint except the auth ones needs a token, and users only ever see the projects they’re members of. A second version of the API runs next to the first at /taskflow/api/v2/, and a pytest suite covers the rules that matter.
NinjaAPI, schemas and the interactive docs are familiar. That also means you’ve finished preps 01-06 and our Channels tutorial, and you have your locallibrary repository: a uv project with manage.py, pyproject.toml, and uv.lock at its root, the locallibrary_config project package, and the catalog and chat apps. Every command in this tutorial is run from that root folder.int | None and list[int]. Your locallibrary uses Python 3.14.The only packages TaskFlow needs are Django and Django Ninja, and both are already in your pyproject.toml: Django since Prep 01, and Django Ninja since the Quickstart. You can check:
uv run python -c "import django, ninja; print(django.__version__, ninja.__version__)"
Everything else, including JSON Web Tokens, is written with the standard library. Part 7 adds pytest and pytest-django as development tools. The database is the same SQLite db.sqlite3 your catalog and chat use, and all of TaskFlow’s views are synchronous.
... where the rest of the file stays the same.PATCH updates and status transitions.FilterSchema for tasks, safe sort orders, and page-number and cursor pagination.TestClient, covering validation, permissions, filters and uploads.Ready? Start with Part 1: Project setup & layout.
This is a copy of the official Django Ninja tutorial, changed to match the way we have worked in our fork of the MDN Django Tutorial, our Channels tutorial, and our Django Ninja Quickstart:
projects, tasks, and accounts apps go into your existing locallibrary repository, next to catalog and chat. Everywhere the original edits taskflow/settings.py or taskflow/urls.py, this copy edits locallibrary_config/settings.py or locallibrary_config/urls.py, and only adds lines.taskflow is a plain package. In the original, taskflow is the Django project package. Here it’s a small package next to manage.py that holds only taskflow/api.py, so imports like from taskflow.api import api work exactly as in the original./taskflow/api/, not /api/. Your Quickstart API already uses /api/. Every TaskFlow URL in this copy starts with /taskflow/api/, and version 2 is at /taskflow/api/v2/. Paths inside TestClient calls are relative to the API, so they’re the same as in the original.NinjaAPI sets urls_namespace=. That keeps TaskFlow’s URL names apart from the Quickstart API’s. Part 8 explains why.pip install becomes uv add (uv add --dev for pytest), and every command is run with uv run.db.sqlite3. The original suggests it in Part 2 so the ids match. Yours holds your catalog and chat data, so this copy looks users up by username where the original uses their ids.media/ to your .gitignore. The original assumes it’s already there.test_db.sqlite3 file. That’s the TEST database setting you added in Part 4 of the Channels tutorial.This copy follows the Django Ninja documentation as of 2026-10-19 (Django Ninja 1.7.1), on Django 6.1 and Python 3.14.
The original tutorial is Copyright (c) 2025 Vitaliy Kucheryaviy, and is used under its MIT license.
One router per app, schemas from models, and task URLs nested under projects.
Assignees and labels as nested output, computed and annotated fields, and fewer queries.
Status and priority choices, field and model validators, PATCH updates and status transitions.
JWT tokens from scratch, project memberships with roles, and object-level rules for comments.
A FilterSchema for tasks, safe sort orders, and page-number and cursor pagination.
Multipart uploads with form fields, file validation, media files and absolute URLs.
A pytest suite with fixtures and TestClient, covering validation, permissions, filters and uploads.
Descriptions, examples and deprecations in the docs, and a version 2 next to version 1.