Metadata-Version: 2.4
Name: drf-idempotency
Version: 1.0.0
Summary: Stripe-style idempotency keys for Django REST Framework with pluggable Redis/database backends
Project-URL: Homepage, https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/
Project-URL: Documentation, https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/getting-started/
Project-URL: Repository, https://github.com/MahmoudGShake/MahmoudPackages/tree/master/drf-idempotency
Project-URL: Issues, https://github.com/MahmoudGShake/MahmoudPackages/issues
Project-URL: Changelog, https://github.com/MahmoudGShake/MahmoudPackages/blob/master/drf-idempotency/CHANGELOG.md
Author-email: Mahmoud Gamal <mahmoudgshaker2018@gmail.com>
Maintainer-email: Mahmoud Gamal <mahmoudgshaker2018@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,django,djangorestframework,drf,idempotency,idempotency-key,redis,reliability
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: django<5.3,>=4.2
Requires-Dist: djangorestframework>=3.14
Provides-Extra: dev
Requires-Dist: black>=24.10.0; extra == 'dev'
Requires-Dist: build>=1.2.0; extra == 'dev'
Requires-Dist: django-stubs[compatible-mypy]>=5.1.0; extra == 'dev'
Requires-Dist: djangorestframework-stubs[compatible-mypy]>=3.15.0; extra == 'dev'
Requires-Dist: fakeredis>=2.24.0; extra == 'dev'
Requires-Dist: hypothesis>=6.112.0; extra == 'dev'
Requires-Dist: mkdocs-material>=9.5.0; extra == 'dev'
Requires-Dist: mkdocs>=1.6.0; extra == 'dev'
Requires-Dist: mkdocstrings[python]>=0.26.0; extra == 'dev'
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: pre-commit>=3.8.0; extra == 'dev'
Requires-Dist: pytest-benchmark>=4.0.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
Requires-Dist: pytest-django>=4.9.0; extra == 'dev'
Requires-Dist: pytest-xdist>=3.6.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: redis>=5.0; extra == 'dev'
Requires-Dist: ruff>=0.6.9; extra == 'dev'
Requires-Dist: tox-gh-actions>=3.2.0; extra == 'dev'
Requires-Dist: tox>=4.21.0; extra == 'dev'
Requires-Dist: twine>=5.1.0; extra == 'dev'
Requires-Dist: types-redis; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.26.0; extra == 'docs'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: test
Requires-Dist: fakeredis>=2.24.0; extra == 'test'
Requires-Dist: hypothesis>=6.112.0; extra == 'test'
Requires-Dist: pytest-benchmark>=4.0.0; extra == 'test'
Requires-Dist: pytest-cov>=5.0.0; extra == 'test'
Requires-Dist: pytest-django>=4.9.0; extra == 'test'
Requires-Dist: pytest-xdist>=3.6.0; extra == 'test'
Requires-Dist: pytest>=8.3.0; extra == 'test'
Description-Content-Type: text/markdown

# drf-idempotency

[![PyPI version](https://img.shields.io/pypi/v/drf-idempotency.svg)](https://pypi.org/project/drf-idempotency/)
[![Python versions](https://img.shields.io/pypi/pyversions/drf-idempotency.svg)](https://pypi.org/project/drf-idempotency/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

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 Conflict` or 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 --strict` clean.

## Installation

```bash
pip install drf-idempotency
pip install drf-idempotency[redis]  # if using the Redis backend
```

## Quick Start

```python
# settings.py
INSTALLED_APPS = [..., "drf_idempotency"]
MIDDLEWARE = [..., "drf_idempotency.middleware.IdempotencyMiddleware"]

IDEMPOTENCY = {
    "BACKEND": "drf_idempotency.backends.database.DatabaseBackend",
}
```

```bash
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](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/getting-started/)
- [Installation](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/installation/)
- [Configuration](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/configuration) / [Settings](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/settings)
- [Quick Start](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/quickstart)
- [Advanced Usage](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/advanced-usage)
- [Architecture](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/architecture)
- [API Reference](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/api-reference)
- [Examples](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/examples)
- [Common Patterns](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/common-patterns)
- [Performance](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/performance)
- [Security](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/security)
- [Testing](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/testing)
- [Deployment](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/deployment)
- [FAQ](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/faq)
- [Troubleshooting](https://mahmoudgshake.github.io/MahmoudPackages/drf-idempotency/troubleshooting)

## Contributing

Contributions are welcome — see [CONTRIBUTING.md](https://github.com/MahmoudGShake/MahmoudPackages/blob/master/drf-idempotency/CONTRIBUTING.md).

## License

MIT — see [LICENSE](https://github.com/MahmoudGShake/MahmoudPackages/blob/master/drf-idempotency/LICENSE).
