A production-style RESTful blog / CMS API built with Django and Django REST Framework. It ships JWT authentication, full CRUD for posts, categories, tags and comments, object-level permissions, filtering, search, pagination, and auto-generated OpenAPI documentation.
- JWT authentication (access / refresh tokens) via
djangorestframework-simplejwt. - User registration with password confirmation and validation.
- Posts, Categories, Tags, Comments exposed as REST resources through
DRF
ViewSets and routers. - Public read, authenticated write — anonymous clients can browse published content; writing requires a token.
- Object-level permissions — a custom
IsAuthorOrReadOnlypermission ensures only a post's (or comment's) author can edit or delete it. - Draft / published workflow — drafts are visible only to their author.
- Filtering, search and ordering on posts by category, tag, status and
author (
django-filter+ DRFSearchFilter/OrderingFilter). - Pagination (page-number based, 10 items per page).
- Slug auto-generation with collision-safe uniqueness.
- Nested read serializers with flat write serializers.
- OpenAPI 3 schema and Swagger UI via
drf-spectacular. - Seed command for instant sample data.
- Test suite built on DRF's
APITestCase.
| Layer | Technology |
|---|---|
| Language | Python 3.12 |
| Framework | Django 5.x |
| API | Django REST Framework |
| Auth | djangorestframework-simplejwt (JWT) |
| Filtering | django-filter |
| API docs | drf-spectacular (OpenAPI + Swagger UI) |
| Database (dev) | SQLite |
django-blog-api/
├── manage.py
├── requirements.txt
├── config/ # project settings, URLs, WSGI/ASGI
├── accounts/ # registration, JWT, profile
├── blog/ # posts, categories, tags, comments + seed command
└── tests/ # APITestCase suites
git clone https://github.com/mooceanstudio/django-blog-api.git
cd django-blog-api
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activatepip install -r requirements.txtpython manage.py migratepython manage.py seedSeeded users share the password seedPass123! (usernames: ada, linus,
grace). Create a superuser for the admin with python manage.py createsuperuser.
python manage.py runserverThe API is now available at http://127.0.0.1:8000/.
- Swagger UI: http://127.0.0.1:8000/api/docs/
- OpenAPI schema: http://127.0.0.1:8000/api/schema/
All resource endpoints are namespaced under /api/.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/auth/register/ |
Register a new user | Public |
| POST | /api/auth/token/ |
Obtain access + refresh tokens | Public |
| POST | /api/auth/token/refresh/ |
Refresh an access token | Public |
| POST | /api/auth/token/verify/ |
Verify a token | Public |
| GET | /api/auth/profile/ |
Current user's profile | Bearer |
| GET | /api/posts/ |
List published posts (+ own drafts) | Public |
| POST | /api/posts/ |
Create a post | Bearer |
| GET | /api/posts/{slug}/ |
Retrieve a post | Public |
| PUT | /api/posts/{slug}/ |
Replace a post (author only) | Bearer |
| PATCH | /api/posts/{slug}/ |
Update a post (author only) | Bearer |
| DELETE | /api/posts/{slug}/ |
Delete a post (author only) | Bearer |
| GET | /api/categories/ |
List categories | Public |
| POST | /api/categories/ |
Create a category | Bearer |
| GET | /api/categories/{slug}/ |
Retrieve a category | Public |
| GET | /api/tags/ |
List tags | Public |
| POST | /api/tags/ |
Create a tag | Bearer |
| GET | /api/comments/ |
List comments | Public |
| POST | /api/comments/ |
Create a comment | Bearer |
| DELETE | /api/comments/{id}/ |
Delete a comment (author only) | Bearer |
| Query parameter | Example |
|---|---|
category (slug) |
/api/posts/?category=engineering |
category_id |
/api/posts/?category_id=1 |
tag (slug) |
/api/posts/?tag=django |
author (username) |
/api/posts/?author=ada |
status |
/api/posts/?status=published |
search |
/api/posts/?search=rest+api |
ordering |
/api/posts/?ordering=-created_at |
page |
/api/posts/?page=2 |
Register a user:
curl -X POST http://127.0.0.1:8000/api/auth/register/ \
-H "Content-Type: application/json" \
-d '{"username":"sam","email":"sam@example.com","password":"strongPass123!","password_confirm":"strongPass123!"}'Obtain a token:
curl -X POST http://127.0.0.1:8000/api/auth/token/ \
-H "Content-Type: application/json" \
-d '{"username":"sam","password":"strongPass123!"}'The response contains access and refresh tokens. Use the access token as a
Bearer credential:
TOKEN="<access-token>"
curl -X POST http://127.0.0.1:8000/api/posts/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"My First Post","body":"Hello, world!","status":"published"}'Refresh an expired access token:
curl -X POST http://127.0.0.1:8000/api/auth/token/refresh/ \
-H "Content-Type: application/json" \
-d '{"refresh":"<refresh-token>"}'python manage.py testThe suite covers the authentication flow, post CRUD, object-level permission enforcement, comment permissions, and filtering / search / ordering.
Released under the MIT License. Copyright (c) 2026 mooceanstudio.