Use better-route 0.5.0 AtomicIdempotencyMiddleware for high-side-effect write endpoints where concurrent duplicate requests must not execute twice. Triggers on AtomicIdempotencyMiddleware, WpdbAtomicIdempotencyStore, ArrayAtomicIdempotencyStore, AtomicIdempotencyStoreInterface, idempotency_in_progress, Idempotency-Key for payment/order/subscription/account-like writes, or when reviewing retry-safe REST endpoints.
Scanned 6/5/2026
Install via CLI
openskills install Lonsdale201/wp-agent-skills---
name: br-atomic-idempotency
description: Use better-route 0.5.0 AtomicIdempotencyMiddleware for high-side-effect write endpoints where concurrent duplicate requests must not execute twice. Triggers on AtomicIdempotencyMiddleware, WpdbAtomicIdempotencyStore, ArrayAtomicIdempotencyStore, AtomicIdempotencyStoreInterface, idempotency_in_progress, Idempotency-Key for payment/order/subscription/account-like writes, or when reviewing retry-safe REST endpoints.
---
# better-route: Atomic idempotency
Use this when a write route must reserve an `Idempotency-Key` before the handler runs. This is stronger than `IdempotencyMiddleware`, which is still useful as a replay cache but stores only after handler completion.
## Pick the middleware
```php
use BetterRoute\Middleware\Write\AtomicIdempotencyMiddleware;
use BetterRoute\Middleware\Write\WpdbAtomicIdempotencyStore;
register_activation_hook(__FILE__, function (): void {
(new WpdbAtomicIdempotencyStore())->installSchema();
});
$atomic = new AtomicIdempotencyMiddleware(
store: new WpdbAtomicIdempotencyStore(),
ttlSeconds: 900,
requireKey: true
);
```
Use `WpdbAtomicIdempotencyStore` in production. `ArrayAtomicIdempotencyStore` is for tests and local single-process checks only.
## Route pattern
```php
$router->post('/actions/confirm', $handler)
->middleware([$jwt, $atomic])
->protectedByMiddleware('bearerAuth');
```
Order matters: auth first, atomic idempotency after auth. The default storage key includes route path, authenticated identity, and the client key.
## Behavior
- First matching request reserves the key, then runs the handler.
- Same key and same fingerprint while the first request is running returns `409 idempotency_in_progress`.
- Same key and same fingerprint after completion replays the stored response.
- Same key with a different fingerprint returns `409 idempotency_conflict`.
- Missing key returns `400 idempotency_key_required` when `requireKey: true`.
- Thrown exceptions release the reservation by default so a client can retry.
## Rules
- Use this for side-effectful writes. Keep `IdempotencyMiddleware` for low-risk replay caching or backwards-compatible adoption.
- Install the DB schema on activation before the route is used.
- Keep TTL long enough for realistic client retries.
- Return `BetterRoute\Http\Response` when clients need replay headers such as `Idempotency-Replayed: true`.
- Use a custom `keyResolver` for tenant-scoped APIs where user ID alone is not enough.
- Use a custom `fingerprintResolver` if query/body defaults include unstable values.
## Source refs
- `libraries/better-route/src/Middleware/Write/AtomicIdempotencyMiddleware.php`
- `libraries/better-route/src/Middleware/Write/WpdbAtomicIdempotencyStore.php`
- `libraries/better-route/src/Middleware/Write/AtomicIdempotencyStoreInterface.php`
- `libraries/better-route/src/Middleware/Write/AtomicIdempotencyRecord.php`
- `libraries/better-route/tests/WriteSafetyMiddlewareTest.php`
No comments yet. Be the first to comment!