drf-idempotency
Stripe-style Idempotency-Key support for Django REST Framework: retry a
POST/PATCH/PUT safely and get back the exact same response, with no
risk of double-processing — even under concurrent retries.
POST /payments/ HTTP/1.1
Idempotency-Key: 6f7a1e3e-2c9d-4b1a-9d0a-6a6f2b6b9b3a
{"amount": 2000, "currency": "usd"}
Retry the exact same request (same key, same body) as many times as you
like — you get the same 201 Created response every time, and the
payment is only ever created once.
Why
Networks fail. Clients time out and retry. Without idempotency keys, a
retried POST can create a duplicate resource (a double charge, a
duplicate order). Stripe popularized the Idempotency-Key header pattern
to solve this cleanly at the API layer; this package brings the same
guarantees to any Django REST Framework project.
Features
- Middleware for automatic, project-wide idempotency handling, and a decorator for per-view opt-in.
- Pluggable storage backends: Redis (atomic
SET NX) and database (unique-constraint +get_or_create), with a documented interface for writing your own. - Response replay: byte-for-byte identical status code, headers, and body on retry — the view is never re-executed.
- Race-condition safe: concurrent requests with the same key never
both execute the view; the loser gets
409 Conflictor waits, based on configuration. - Request fingerprinting: reusing a key with a different request
body is rejected (
422) rather than silently replaying the wrong response. - TTL and automatic cleanup: Redis keys expire natively; a management command cleans up expired database records.
- Status tracking: query whether a key is in progress, completed, or failed.
- Fully typed, PEP 561 compatible,
mypy --strictclean.
Installation
pip install drf-idempotency
pip install drf-idempotency[redis] # if using the Redis backend
Quick Start
# settings.py
INSTALLED_APPS = [..., "drf_idempotency"]
MIDDLEWARE = [..., "drf_idempotency.middleware.IdempotencyMiddleware"]
IDEMPOTENCY = {
"BACKEND": "drf_idempotency.backends.database.DatabaseBackend",
}
python manage.py migrate
That's it — every POST/PUT/PATCH request carrying an
Idempotency-Key header is now automatically deduplicated.
Documentation
Full documentation: https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/
- Getting Started
- Installation
- Configuration / Settings
- Quick Start
- Advanced Usage
- Architecture
- API Reference
- Examples
- Common Patterns
- Performance
- Security
- Testing
- Deployment
- FAQ
- Troubleshooting
Contributing
Contributions are welcome — see CONTRIBUTING.md.
License
MIT — see LICENSE.
Metadata
Release files for drf-idempotency 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| drf_idempotency-1.0.0.tar.gz | 47.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| drf_idempotency-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 74.4 kB
Release files / drf_idempotency-1.0.0.tar.gz
| Download URL | drf_idempotency-1.0.0.tar.gz |
|---|---|
| Size | 47.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cea68803f04676d1edd893dc85ad0b9bf93bf97fc88d426b4df69b453af50544
|
|
BLAKE2b-256 checksum How to use checksums |
4fe62ced626d267b2656a9d77e0a082e2aa60b2c40f8fdf6b3fcf6309eae41d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / drf_idempotency-1.0.0-py3-none-any.whl
| Download URL | drf_idempotency-1.0.0-py3-none-any.whl |
|---|---|
| Size | 26.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ee5b59fbfae16e6c37467130ec3cbb9266ed0b14dee5f5e1466461c7c0415bdb
|
|
BLAKE2b-256 checksum How to use checksums |
75ae41eda7dfd0af9e61b1e1ec4a733bb3a0738543618ebec334b838bb91b069
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|