Guide

A client sends a request and the connection drops. Did the server act on it? If the client sends it again, will anything break? HTTP answers these questions with two promises per method. A method is safe if it has no side effects. A method is idempotent if doing it twice has the same effect as doing it once.

Concept

Before you start, a method is the verb of an HTTP request, such as GET or POST. A retry is sending the same request again after a failure. Both are covered in HTTP.

The big idea

Good REST design treats URLs as resources, which are things like files, users or payments. You act on them with a small set of verbs. Some conventions keep this predictable.

  • Plural nouns and stable IDs. Use /files/fl_9, not /getFile?id=9. Prefixed IDs such as fl_ and u_ make logs readable and catch a user ID passed where a file ID was expected.
  • Shallow nesting. List files under a folder, but fetch one file directly at /files/fl_9.
  • One naming style. Use RFC 3339 timestamps in UTC, and store money as integer minor units such as cents, never floats.

The method table below is what callers and proxies rely on when they decide whether to retry.

Walking through it

MethodSafeIdempotentTypical use
GETYesYesRead
PUTNoYesReplace a resource at a known URL
DELETENoYesDelete. A second call returns 404 or 204, and the state is the same
PATCHNoNot guaranteedPartial update
POSTNoNoCreate, or trigger an action

POST is the risky one. Follow a payment through a timeout:

  1. The client sends POST /v1/payments for 4,200 cents.
  2. The server charges the card, but the response is lost in the network.
  3. The client times out and can't tell whether the charge happened.
  4. A plain retry charges the card a second time.

The fix is an idempotency key. The client creates one key per logical operation and sends it in a header. The server stores the result under that key and returns the stored result for any retry.

HTTP
POST /v1/payments HTTP/1.1
Idempotency-Key: 6f1c2a9e-8d4b-4f8e-9a51-2c7e0b3d1f44
Content-Type: application/json

{"amount": 4200, "currency": "usd", "source": "card_abc"}

Now the retry finds the stored result for that key, so the card is charged once. The idempotency page covers how the server stores those results.

Common mistakes

  • Retrying a POST without a key. The client can't know whether the first attempt worked, so the retry may repeat the action.
  • Generating a new key on each retry. The key names one logical operation. A new key per attempt gives you no protection.
  • Assuming PATCH is idempotent. The HTTP spec doesn't guarantee it. Check what your PATCH does when it runs twice.

Check yourself

  1. Is DELETE idempotent? Yes. The second call finds nothing to delete, so the end state is the same even though the status code may change.
  2. Why is a timed-out POST dangerous to retry? The client doesn't know if the first attempt succeeded, so a retry can repeat the side effect unless an idempotency key is sent.

Key takeaways

  • Safe means no side effects. Idempotent means the same end state after one call or many.
  • GET, PUT and DELETE are idempotent. POST is not, so it needs an idempotency key to retry safely.
  • Give every create or action endpoint a key, and store its result for retries.

Further learning

The idempotency page explains how to store results for keys.

Next, read about cursor pagination, which applies the same care to lists.