𝗥𝗮𝗰𝗶𝗻𝗴 𝗮 𝗡𝗲𝘅𝘁.𝗷𝘀 𝗔𝗣𝗜 𝗥𝗼𝘂𝘁𝗲

A single-use coupon should only work once.

But in this scenario, thirty people can use it at the exact same time.

This happens because of a Time-of-Check Time-of-Use (TOCTOU) race condition.

The error occurs in two steps:

  • Step 1: The server reads the database to check the used count.
  • Step 2: The server sends a second command to increment that count.

If you send many requests at once, the server handles them all in the middle of these two steps.

Many requests read the count while it is still zero. They all pass the check. They all apply the discount. They all increment the counter.

The result? A coupon meant for one person is used thirty times.

This is easy to miss during code reviews. The logic looks perfect when you read it line by line. The flaw only appears when you consider speed and concurrency.

How to fix it:

Stop using separate read and write commands. Instead, use a single atomic operation.

In Prisma, you can use updateMany with a condition in the WHERE clause:

• The database checks the condition and performs the update in one single motion. • If the count is already at the limit, the update fails immediately. • No other request can slip in between the check and the write.

Another option is using a database transaction. This locks the data so no other request can touch it until you finish.

For SQLite, the single update command is the most reliable method.

Always ask yourself: what happens if two requests hit this logic at the same millisecond?

Source: https://dev.to/oopssec-store/racing-a-nextjs-api-route-coupon-abuse-with-prisma-and-sqlite-3gma