If you’ve ever been in a situation where you paid for pizza online, mustered the courage to begin and complete that transaction and accidentally said “you too” in response to “enjoy your meal. It’ll soon arrive” then you know that the only thing worse than the embarrassment from that conversation is realising that the pizza you ordered for say, $20 (great economy) has ensured you get charged $40 or worse $60. You know what’s worse than that still? Now you have to call the same person and complain about being triple-charged.
You may be amused now as you sit comfortably indoors with your home-cooked meal, but it is an occurrence that may make you lay curses on the poor franchise when, in fact, you may have to shift your blame to the backend developers responsible for the feature.
Idempotency is an interesting concept. It is based on the fact that acting many times provides the same result as doing it just once. It is important because there are some actions that should not be repeated in a relatively short time, and if done anyway, the developers should assume it to be in error. When building applications, we must prepare for contingencies. You build the backend expecting that maybe a child has the phone and clicked on the “pay for pizza” button twelve times, or an absent-minded mother of eight forgot she paid for her purchase a second ago and clicked the button two more times. Perhaps the WIFI was acting up, so she made another attempt. The point is, without enforcing idempotency for particular endpoints, there would be unimaginable chaos and a lot of pizzas. How does a backend developer avoid such happenings?
Please note that the examples in this article are based on Node.js and SQL. However, you can still follow along.
Where You Find Idempotency
The GET Route
Certain endpoints enforce idempotency by their very definition. The GET route, for instance, is intuitively idempotent and Safe (meaning, it does not change anything). If you head to a pizza site and request the menu listing, you get the menu listing every time. The result is the same irrespective of how many times the endpoint is called. Whether you click on it 3 times or 30 times, you still get the menu.
app.get('/pizza-menu', (req, res) => {
res.json(['Chicken', 'Pepperoni', 'Goat', 'Special?'])
})
// GET requests are idempotent - give the same result no matter how often called
Use our Online Code Editor
The PUT Route
For a PUT request called thrice, that update will occur thrice. If you would rather change your favourite online order, for instance, from “chicken” to “special?” you’d be able to, and if you changed your mind after finding out what “special?” was, you can update as well. However, calling the same request results in the same result, and that makes PUT idempotent though not Safe (meaning that it changes state, but just once when called).
Here, we’re changing a state of something (your favourite order). If there is a sudden drop in internet connection mid-order and you try again, no harm has occurred because your favourite order remains “special.” We can therefore say that PUT requests are idempotent.
app.put('/user/favorite', (req, res) => {
const { favoritePizza } = req.body
user.favorite = favoritePizza
res.status(200).json({message: `Favorite updated to ${favoritePizza}`})
})
Use our Online Code Editor
The DELETE Route
The delete route is idempotent, and the reason is quite intuitive. When you delete an order, most well-built apps, at least, don’t give you the option to delete again. However, if, for some inexplicable reason, you find that the button still exists for deleting, perhaps a pizza order, then pushing it again 6 more times does not change the outcome. The result is the same, and you’re not having any pizza. The app will typically report a 404 error, meaning the pizza order no longer exists. The result is the same, hence DELETE requests are idempotent and not Safe (similar to PUT requests).
app.delete('/order/:id', (req, res) => {
deleteOrderFromDb(req.params.id)
res.status(204).json({message: "Order has been successfully deleted"})
})
Use our Online Code Editor
Where You Don’t Find Idempotency
The POST Route
The POST route is pretty much it for routes that are definitely not idempotent. POST requests allow for creating something, usually by inserting data. This could be an order, money for purchase and so on. If not handled correctly, every request made may repeat an order or a purchase. This is why, as a backend developer who would rather avoid problems, especially with sensitive data, you consider the POST request’s lack of idempotency in your implementations.
app.post('/orders', (req, res) => {
const { pizzaId, quantity } = req.body
const newOrder = db.orders.create({ pizzaId, quantity })
res.status(201).json({message: "Order created successfully", data: newOrder})
})
Use our Online Code Editor
This POST request will run as many times as it is called, and that would usually not end well, depending on the use case. Now we know the menace a POST request can be in matters of idempotency, how do we avoid the trouble?
Making POST Requests Idempotent
Unique Indexing
Imagine a particular customer, Fred, likes pizza so much that the day he orders, no one else gets any. You may argue that’s an advantage to the franchise, but maybe the franchise hates this too. As a backend developer working on their online platform, they could decide to place a unique constraint on the user_id and status fields so that there can only be one active order per user at a time. This is one way to prevent Fred from clicking the order button and creating many orders at once.
CREATE UNIQUE INDEX
idx_one_active_order
ON orders (user_id, status)
WHERE status = 'pending'
Use our Online Code Editor
Upsert Logic
Another way to ensure a POST request is idempotent is by implementing an upsert strategy. What this means is that if an ID already exists, instead of adding new data to your database, you simply update the existing data with that ID. Picture making pizza orders and consistently pushing the “Add to Cart” button for the same pizza type. Upsert logic does not allow for creating new rows. Instead, it ensures that the count on that particular order is increased. While the first POST request creates the order, subsequent requests just update the quantity of the order.
This way, you can create logic that does not rush to triple a bill but actually recognises a repeat and does something about it.
INSERT INTO orders (id, pizza_id, quantity)
VALUES ($1, $2, $3)
ON CONFLICT (id)
DO UPDATE SET quantity = orders.quantity + EXCLUDED.quantity
Use our Online Code Editor
Conclusion
Idempotency is an important concept that a backend developer should have in his or her arsenal. It can help to avoid problems where a POST request runs more times than it needs to, leading to complications such as double-billing. You’ve learnt that GET, PUT, and DELETE requests are idempotent while POST requests are not.
While this article uses pizza orders to explain this concept, a bad handle on idempotency can have far-reaching consequences in finance, business and in several other fields. It’s therefore important to pay close attention to app requirements, so you recognise where to enforce idempotency in your projects.
Have a great one!!!
Author: Amanda Ene Adoyi
Thank you for being a part of the community
Before you go:
Whenever you’re ready
There are 4 ways we can help you become a great backend engineer:
- The MB Platform: Join thousands of backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learning and set schedules, and solve backend engineering tasks, exercises, and challenges.
- The MB Academy: The “MB Academy” is a 6-month intensive Advanced Backend Engineering Boot Camp to produce great backend engineers.
- Join Backend Weekly: If you like posts like this, you will absolutely enjoy our exclusive weekly newsletter, sharing exclusive backend engineering resources to help you become a great Backend Engineer.
- Get Backend Jobs: Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board.

Top comments (0)