What You'll Learn
- Why APIs need versioning and the three common ways to do it
- What idempotency means and which HTTP methods are idempotent
- How idempotency keys make POST safe to retry
- How to design APIs that survive changes and network failures
Why This Matters
APIs change. Fields get added, renamed, removed. Behavior shifts. Without versioning, every change breaks existing clients. Without idempotency, every network retry risks duplicating side effects (double charges, double orders, double emails). Together, these two concepts are what separate a production-grade API from a prototype.
Part 1 — API Versioning
Versioning lets you introduce breaking changes without breaking existing clients. Old clients keep using /v1/; new clients use /v2/.
The three common styles
1. Path versioning (most common)
GET /v1/users/123
GET /v2/users/123
Simple, explicit, visible in logs and analytics. Used by GitHub, Stripe, Twitter.
2. Header versioning
GET /users/123
Accept: application/vnd.example.v2+json
URL stays clean; version is in the Accept header. Used by GitHub (for some endpoints) and many enterprise APIs. Harder to test in a browser.
3. Query parameter versioning
GET /users/123?version=2
Easy to implement but easy to forget. Breaks caching because the URL is the same for different versions.
Which to choose?
| Style | Pros | Cons |
|---|---|---|
| Path | Simple, explicit, cacheable | URLs change between versions |
| Header | Clean URLs | Hard to test, easy to forget |
| Query | Easy | Breaks caching, easy to forget |
For most APIs, path versioning is the best default.
What counts as a breaking change?
- Removing a field from the response
- Renaming a field
- Changing a field's type (string → number)
- Changing a status code (200 → 201)
- Making an optional parameter required
- Changing default behavior (e.g., pagination size)
Non-breaking changes (adding a new optional field, adding a new endpoint) do not require a new version.
Part 2 — Idempotency
Idempotency means: calling the same operation multiple times has the same effect as calling it once.
Idempotent HTTP methods
| Method | Idempotent? | Safe (no side effects)? |
|---|---|---|
| GET | Yes | Yes |
| PUT | Yes | No |
| DELETE | Yes | No |
| POST | No | No |
| PATCH | Not guaranteed | No |
Why does this matter?
Networks fail. Timeouts happen. Clients retry. If a client calls POST /orders to place an order, the network drops the response, and the client retries — did the first call succeed? Should the retry create a second order?
For idempotent methods, this is not a problem. PUT /users/123 with the same body always results in the same final state, whether called once or ten times. DELETE /users/123 is the same — after the first call, the user is gone; subsequent calls return 404 but do not change anything.
But POST /orders is not idempotent. Retrying creates multiple orders. That is bad for e-commerce, payments, and any operation with side effects.
The solution: idempotency keys
The client generates a unique ID (a UUID) and sends it with the request. The server stores the ID and the response. If the same ID comes in again, the server returns the stored response instead of re-executing.
POST /payments HTTP/1.1
Idempotency-Key: 7c8d3f1e-2a4b-4c5d-9e8f-1a2b3c4d5e6f
Content-Type: application/json
{"amount": 1000, "currency": "INR"}
First call: server processes the payment, returns 201 Created, stores the response keyed by the idempotency key.
Retry with the same key: server recognizes the key, returns the same 201 Created response — does not create a second payment.
How to Implement Idempotency
- Client generates a UUID for each logical operation.
- Client sends it in a header (commonly
Idempotency-Key). - Server stores the key + the response for some time (24 hours is typical).
- On retry with the same key: server returns the stored response without re-executing.
- On retry with the same key but different body: server returns 409 Conflict — the key is in use with different parameters.
Common Mistakes
- Not versioning at all. "We'll add it when we need it" never works. Start with
/v1/from day one. - Renaming fields in a non-breaking version. Adding fields is fine; renaming or removing requires a new version.
- Assuming POST is idempotent. It is not. Without an idempotency key, retries create duplicates.
- Using the same idempotency key for different operations. Each logical operation needs a fresh UUID.
- Forgetting that DELETE is idempotent.
DELETE /users/123called twice should not error the second time — it should return 404 (already gone) or 204 (idempotent success).
Practical Exercise (5 minutes)
Classify each operation as idempotent or not:
GET /users/123POST /usersPUT /users/123PATCH /users/123with body{"email": "new@x.com"}DELETE /users/123POST /paymentswith an idempotency key
Answers: 1. Idempotent. 2. Not idempotent. 3. Idempotent. 4. Idempotent in this case (setting one field to a fixed value). 5. Idempotent. 6. Idempotent because of the key.
Mini Challenge
Design an idempotency strategy for a "send email" endpoint. The client should be able to retry safely without sending duplicate emails. What header does the client send? What does the server store? How does it handle a retry? What if the client retries with the same key but a different email body?
Key Takeaways
- Versioning lets you make breaking changes safely. Path versioning (
/v1/) is the most common style. - Breaking changes: removing/renaming fields, changing types or status codes, making optional params required.
- Idempotency: same operation called multiple times = same final state.
- GET, PUT, DELETE are idempotent. POST is not. PATCH is conditional.
- For non-idempotent operations (POST /payments), use an idempotency key header.
Previously: Lesson 19 covered pagination, filtering, sorting.
Today: You learned how to keep APIs stable across versions and safe across retries.
Next: Lesson 21 closes Module 3 with REST error handling and status code conventions.
FAQ
How long should an idempotency key be stored?
Typically 24 hours. Long enough to cover network retries, short enough to keep storage bounded. Stripe stores them for 24 hours; other APIs use 24 hours to 7 days. Document the window for your API.
Do I need a new version for every change?
No. New endpoints, new optional fields, and new response fields are non-breaking — they go in the current version. Only breaking changes (removing or renaming) require a new version. Most APIs go years between major versions.
Comments
Comments
Post a Comment