REST users rejoice! Conditional requests are here!

How would you increment an integer in the Realtime Database? It's not always as easy as just grabbing a value, adding one, and writing it back again. When multiple users are simultaneously reading, updating and writing the same value, some users' writes could get "lost" due to race conditions.

Let's say you're using the Realtime Database to store upvotes on videos, and two of your users want to upvote the "Silly Cat Video" which currently stands at ten upvotes. Each user reads the value '10' from the database. They then independently increment the value locally and write '11' back to the database. "Silly Cat Video" now only has eleven upvotes when it should have twelve.

How do we prevent this? On a mobile or web client, you could use transactions, which have long been supported in our realtime SDKs. But what if you're using the REST API?

Good news for you: we've just added conditional requests to the REST API! While conditional requests work differently than the realtime transactions, we can still use them to safely perform atomic operations.

How to increment requests

Step 1: Request Data and ETag

Request:

# -i returns headers in the response, -H sets a header
curl -i 
-H 'X-Firebase-ETag: true' 
'https://<your-database>.firebaseio.com/videos/silly_cat_video/upvotes.json'

Response:

HTTP/1.1 200 OK
...
ETag: ViJFJowpbyRvgGNPzPJdGeN+mCY=
...

10  // Value at the specified location

Step 2: Write the New Value

Request:

# -X sets the method (defaults to GET), -d provides data to the method
curl -i 
-X PUT 
-d '11' 
-H 'if-match: ViJFJowpbyRvgGNPzPJdGeN+mCY=' 
'https://<your-database>.firebaseio.com/videos/silly_cat_video/upvotes.json' 

If no other users have incremented upvotes.json the PUT request will succeed. If another user has, the request will fail and the response will contain the new ETag and data. Use these to calculate a new upvotes value and retry the PUT request.

But wait, what does all this ETag and if-match stuff mean, and how do they work?

The Realtime Database's conditional REST requests use ETags and the if-match header from the HTTP conditional requests standard. An ETag is a short unique identifier for data at a location: if the data changes, so does its ETag. if-match, when set to an ETag value, tells the database to only complete a request's operation if the data the request would overwrite matches the provided ETag.
Together, these two features enable a Compare and Swap (CAS) paradigm. Compare and Swap uses the two step process we saw in our video upvote example:

  1. Fetch the location you wish to update along with its ETag.

  2. Issue a PUT request with new data in the body and the ETag of the old data in the if-match header.
    If the data at the location changes between Step 1 and Step 2, the database will not complete the operation and will return an error containing the new ETag and data. You can use these values to construct a new PUT request and retry from Step 2.

For more details and to get started check out our REST conditional requests guide.