Why duplicates still happen
Vercel’s retry logic is independent of any client‑supplied header. A retry is triggered when the function returns a 5xx status or times out. If the function performs the write before it records the Idempotency‑Key, the second invocation will repeat the write, creating a duplicate entry.
Step‑by‑Step Fix
- Validate the header early. Reject the request with a 400 if the Idempotency‑Key is missing or malformed.
- Persist the key first. Store the key in a fast, short‑lived store (Redis, KV, or a dedicated DB column) with a TTL that matches the expected retry window. Use an atomic
INSERT … ON CONFLICT DO NOTHING or a transaction that locks the key row. - Check before writing. If the key already exists, skip the write and return the cached response or a 200 with a “duplicate” flag.
- Return 200 only after persistence. Do not send a success status until the key is safely stored and the write has completed.
- Guard with a unique constraint. Add a unique index on the Idempotency‑Key column (or on the combination of fields that uniquely identify the resource). This catches any race that slips through the application logic.
- Log for correlation. Record the Vercel
Request‑Id and the Idempotency‑Key in your logs. This lets you trace retries and confirm that the duplicate path is avoided.
Edge Middleware for Fast Deduplication
You can add a middleware that runs before the function:
export default async function middleware(req) {
const key = req.headers.get('Idempotency-Key');
if (!key) return new Response('Missing key', { status: 400 });
const cached = await KV.get(key);
if (cached) return new Response(cached, { status: 200 });
// Pass through to the function; the function will store the key.
return NextResponse.next();
}
Because the middleware executes before the function, it adds only a few milliseconds of latency for fresh requests. It also guarantees that a retry will hit the cache and never reach the function again.
Handling ISR Background Revalidation
ISR triggers the same endpoint but includes a special header (__vercel_revalidate) or query parameter (?__vercel_revalidate=1). Detect this flag and skip any write logic:
if (req.headers.get('__vercel_revalidate')) {
// Only rebuild the page; do not touch the DB.
return new Response('ISR rebuild', { status: 200 });
}
Alternatively, route ISR calls to a separate handler that only performs the page regeneration.
Verification Checklist
- Send a test request with a known Idempotency‑Key. Confirm the database contains a single record.
- Force a 5xx or timeout on the first attempt and observe that the second attempt does not duplicate the write.
- Check the database for a unique‑constraint violation when you deliberately bypass the key check.
- Review Vercel logs: each retry should show the same Request‑Id and the key should be logged before the write.
Missing Diagnostic Detail
To fine‑tune the strategy, could you tell me which database or KV store you are using to persist the Idempotency‑Key? That will influence the exact atomic operation syntax.