Skip to main content

Idempotency middleware for Django REST Framework

Project description

drf-idem

Читать на русском (Read in Russian)

Idempotency middleware for Django REST Framework. Prevents duplicate request processing using Redis.

When a client sends the same request twice (network retry, double-click, bug), drf-idem detects the duplicate and returns an immediate response — without running your business logic a second time.

Installation

# In a uv-managed project
uv add drf-idem

# Or via pip
pip install drf-idem

Quick Start

1. Add to INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    "drf_idem",
]

2. Add middleware first in the list:

MIDDLEWARE = [
    "drf_idem.middleware.IdempotencyMiddleware",
    ...
]

3. Configure a dedicated Redis cache backend:

CACHES = {
    "default": { ... },
    "drf_idem": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
    },
}

4. Add settings:

DRF_IDEM = {
    "HEADER": "HTTP_X_REQUEST_ID",   # reads X-Request-ID header
    "TTL": 60,                        # seconds, max 60
    "CACHE_BACKEND": "drf_idem",
    "ENDPOINTS": [],                  # empty = check all endpoints
                                      # formats: "POST /path", "/path" (all METHODS), "* /path" (absolutely all)
    "METHODS": ["POST", "PUT", "PATCH", "DELETE"],
}

5. Mount admin stats page in urls.py:

# IMPORTANT: place before path("admin/", admin.site.urls)
urlpatterns = [
    path("admin/drf-idem/", include("drf_idem.urls")),
    path("admin/", admin.site.urls),
    ...
]

Open /admin/drf-idem/stats/ to see duplicate request statistics.

How It Works

The client attaches a unique X-Request-ID header to each request:

POST /api/payments/ HTTP/1.1
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{"amount": 100}

First request — processed normally, key stored in Redis with TTL.

Duplicate request (same X-Request-ID + same method + same path, within TTL):

HTTP/1.1 200 OK
Content-Type: application/json

{"amount": 100}

Business logic is not executed. The exact same HTTP status and body from the original response are returned transparently to the client.

Settings Reference

Key Default Description
HEADER HTTP_X_REQUEST_ID Django META key for the idempotency header (X-Request-IDHTTP_X_REQUEST_ID)
TTL 60 Seconds to remember a request. Capped at 60.
CACHE_BACKEND "drf_idem" Django CACHES alias to use
ENDPOINTS [] Endpoint filter (empty = apply to all). Formats: "METHOD /path", "/path", "* /path"
METHODS ["POST", "PUT", "PATCH", "DELETE"] HTTP methods to check when ENDPOINTS is empty
STATS_TTL 604800 (7 days) TTL for statistics data in Redis

Endpoint Filtering

Use ENDPOINTS to restrict which endpoints are protected:

DRF_IDEM = {
    "ENDPOINTS": [
        "POST /api/payments/",   # only POST to /api/payments/*
        "/api/orders/",          # any method from METHODS to /api/orders/*
        "* /api/critical/",      # all methods to /api/critical/*
    ],
}

Pattern formats:

  • "METHOD /path/prefix" — specific method + path prefix
  • "/path/prefix" — any method from METHODS + path prefix
  • "* /path/prefix" — all methods + path prefix

When ENDPOINTS is empty, all endpoints matching METHODS are checked.

Security

  • Key isolation: Cache keys use SHA-256 of (method, path, request_id) — no injection via separator characters.
  • Input validation: request_id must be ≤ 128 chars and match [A-Za-z0-9\-_./]+. Invalid values return HTTP 400.
  • Atomic writes: Uses cache.add() (Redis SETNX) to eliminate TOCTOU race conditions.
  • Admin protection: Stats page requires is_staff=True.

Admin Statistics

The stats page at /admin/drf-idem/stats/ shows:

  • Top endpoints by duplicate count
  • Timestamp of last duplicate
  • Total Redis memory used by drf_idem:* keys (with warning if > 10 MB)

Development

git clone https://github.com/example/drf-idem
cd drf-idem
uv sync --extra dev
uv run pytest -v

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

drf_idem-0.1.1.tar.gz (12.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

drf_idem-0.1.1-py3-none-any.whl (10.4 kB view details)

Uploaded Python 3

File details

Details for the file drf_idem-0.1.1.tar.gz.

File metadata

  • Download URL: drf_idem-0.1.1.tar.gz
  • Upload date:
  • Size: 12.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for drf_idem-0.1.1.tar.gz
Algorithm Hash digest
SHA256 2b1866da75bac2c8c078e2924e5f7043c92abecc97c487933cdb95e87d812264
MD5 7e6c60808d92477864e26d92746e7234
BLAKE2b-256 d5eca962d050503fcc82330ec4f0ac6b1c24ca45ab6b889df02d0e7ffad42e0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for drf_idem-0.1.1.tar.gz:

Publisher: publish.yml on safonin/drf-idem

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file drf_idem-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: drf_idem-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for drf_idem-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6528298e322e9be52f65a003f549954cd6ecbf17f0002de9b7598d3a93bd6987
MD5 f3e054bfc0c5e8a2f7822af6cf850ade
BLAKE2b-256 a9977a11b227671f4f27dd8b6eabe8f3374450e9d165d66cff6cff24bbca0702

See more details on using hashes here.

Provenance

The following attestation bundles were made for drf_idem-0.1.1-py3-none-any.whl:

Publisher: publish.yml on safonin/drf-idem

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page