Introduction
When building APIs, it’s crucial to understand how different HTTP methods behave when the same request is sent multiple times. This concept is called idempotency — and it helps ensure your API is reliable, safe, and user-friendly.
In this blog, we’ll:
- Explore which HTTP verbs are idempotent and why
- Show simple Node.js/Express code examples for each verb
- Explain how to handle non-idempotent requests like POST safely
- Discuss ETags and caching
- Understand when and how we need to implement idempotency ourselves
Let’s dive in! 🏊♂️
What Is Idempotency? 🤔
Idempotency means making the same API call multiple times results in the same effect as making it once. This prevents accidental duplicates, data corruption, or unexpected side effects.
Imagine this 💡:
“If pressing a button once is good, pressing it twice shouldn’t break things.”
HTTP Verbs and Their Idempotency 🔄
1. GET — Safe & Idempotent ✅
GET requests retrieve data without changing anything.
- Repeating GET calls returns the same data without side effects.
- Perfect for caching and safe retries.
app.get('/items/:id', (req, res) => {
const item = items.find(i => i.id === req.params.id);
if (!item) return res.status(404).send('Item not found');
res.json(item);
});
2. PUT — Idempotent Update ✅
PUT replaces or updates a resource completely.
- Sending the same PUT request multiple times leaves the resource in the same state.
app.put('/items/:id', (req, res) => {
const index = items.findIndex(i => i.id === req.params.id);
if (index === -1) return res.status(404).send('Item not found');
items[index] = { id: req.params.id, ...req.body };
res.json(items[index]);
});
3. DELETE — Idempotent Removal ✅
DELETE removes a resource.
- Repeating a DELETE request for the same resource is safe — the resource is gone after the first call.
app.delete('/items/:id', (req, res) => {
const index = items.findIndex(i => i.id === req.params.id);
if (index === -1) return res.status(404).send('Item not found');
items.splice(index, 1);
res.status(204).send();
});
4. POST — Not Idempotent by Default ❌
POST usually creates new resources.
- Sending the same POST request multiple times typically creates multiple resources — unless we add custom logic.
Making POST Idempotent with Idempotency Keys 🔑
const idempotencyCache = new Map();
app.post('/items', (req, res) => {
const key = req.headers['idempotency-key'];
if (!key) {
return res.status(400).json({ error: 'Idempotency-Key header required' });
}
if (idempotencyCache.has(key)) {
return res.json({
message: 'Returning cached response',
item: idempotencyCache.get(key),
});
}
const newItem = { id: uuidv4(), ...req.body };
items.push(newItem);
idempotencyCache.set(key, newItem);
res.status(201).json({
message: 'Item created successfully',
item: newItem,
});
});
Bonus: PATCH — It Depends 🤷
- PATCH is usually not idempotent because it applies partial updates.
- But it can be made idempotent if applying the same patch leads to the same final state.
Summary Table 📋
HTTP Method | Idempotent? | Description |
---|---|---|
GET | ✅ Yes | Read-only, safe to call repeatedly. |
PUT | ✅ Yes | Update/replace resource, safe retries. |
DELETE | ✅ Yes | Remove resource, repeated calls safe. |
POST | ❌ No | Create resource, duplicates if repeated. Use idempotency keys! |
PATCH | ❌ Usually No | Partial update, may or may not be idempotent based on implementation. |
Do Idempotent Requests Work Out of the Box? 🤝
What HTTP Gives You “Out of the Box” 🛠️
- GET, PUT, DELETE are defined as idempotent in the HTTP spec.
- If your server code sticks to the correct behavior, you get idempotency for free.
- Caching and ETags for GET requests are supported by clients and proxies if you set headers properly.
What You Need to Handle Explicitly ⚙️
-
POST is not idempotent, so:
- Accept an Idempotency-Key header
- Store responses tied to that key
- Reuse the stored result for retries
PATCH and custom methods: implement idempotency logic if needed.
Why Isn’t Everything Idempotent by Default?
Because many operations involve changes that aren’t safe to repeat:
- Creating new resources ✔️ duplicates
- Incrementing counters ✔️ double counts
- Appending logs ✔️ repeated data
Final Thoughts 💡
Idempotency is key to building robust, user-friendly APIs that behave predictably under retries and network glitches.
By understanding how HTTP verbs behave and using tools like idempotency keys and ETags, we can prevent common pitfalls like duplicate orders or payments.
In the upcoming blog, we’ll explore Retrying Failed Requests and implementing the Exponential Backoff Algorithm to make your applications even more resilient. Stay tuned! 🌟
Happy coding! 🚀
Top comments (10)
pretty cool seeing it all laid out like this - i always wonder where you decide enough safety is enough and when you’re just overengineering?
Nicely explained
Just a quick correction: Code for PUT should insert if the ID is not found.
Hey Thanks, I will update the code block
Pretty cool bro ,I do that from frontend,i didn't know that we can optimise it from backend
Insightful!
Real talk, this kinda breakdown is actually huge for keeping my brain straight when coding APIs. Nice.
Thanks for the Crystal clear explanation, really helpful in understanding the concept.
Very informative.
Thanks for the crystal clear explanation, really helpful in understanding the concept of Idempotency.