REST API
Conditional Writes (If-Match / 412)
RequestValidateHard
What you will learn
- Use ETag + If-Match to prevent lost updates under concurrent edits (optimistic concurrency)
- Map the status codes: 428 (precondition required), 412 (precondition failed), 200 (current ETag)
- Always re-read the ETag after a successful write before the next conditional update
Description
Prevent lost updates with optimistic concurrency: a PUT must carry the current ETag via If-Match or it is rejected.
Test Scenario
// Read the current ETag, then prove stale writes are blocked.
const read = await request.get('/api/v1/etag?id=resource1');
const etag = read.headers()['etag'];
const noHeader = await request.put('/api/v1/etag?id=resource1&strict=1', { data: { name: 'x' } });
expect(noHeader.status()).toBe(428); // If-Match is mandatory in strict mode
const stale = await request.put('/api/v1/etag?id=resource1&strict=1', {
headers: { 'If-Match': '"deadbeef"' },
data: { name: 'x' },
});
expect(stale.status()).toBe(412); // someone else changed it first
const ok = await request.put('/api/v1/etag?id=resource1&strict=1', {
headers: { 'If-Match': etag },
data: { name: 'x' },
});
expect(ok.status()).toBe(200);Endpoint
/api/v1/etag?strict=1Testing Tips
- โขGET the resource and capture its ETag response header
- โขIn strict mode (?strict=1) a PUT with no If-Match โ 428 Precondition Required
- โขA PUT with a STALE If-Match โ 412 Precondition Failed; only the current ETag succeeds
// Playwright API Testing - RESTimport { test, expect } from '@playwright/test';test('REST API request', async ({ request }) => {const response = await request.get('/api/v1/resource');expect(response.ok()).toBeTruthy();expect(response.status()).toBe(200);const data = await response.json();expect(data).toBeDefined();});
Challenge ID: rest-conditional-write
2 frameworks available
API Playground
Send requests and inspect responses
Endpoint: /api/v1/etag?strict=1
Press Enter to send