Rectangle 27 1

There is unfortunately, no way to test whether the data exists without actually downloading it via the SDK. Data structures are going to be supreme here (recommended reading: NoSQL Data Structures and you shouldn't be afraid to denormalize a bit of data when optimization and scale are critical.

Generally speaking, you should keep your data well structured so payloads are small and fetch it. If you're fetching something that can't wait for the bytes to be fetched (e.g. games, strange one-off admin ops on very large data sets, et al) then here are a few reasonable approaches to simulate this:

Fetching a list of keys via the REST API

Using the attribute shallow=true in a call to the REST API will prevent loading of a large data set and return only the keys at that path. Note that if you store a million records, they still have to be loaded into memory on the server (slow) and you still have to fetch a million strings (expensive).

So one way to check the existence of data at a path, without actually downloading the data, would be to make a call to the path, such as https://<YOUR-FIREBASE-APP>.firebaseio.com/foo.json?shallow=true, and check whether any keys are returned.

Creating a denormalized index you can query instead

If you really need to squeeze some extra performance and speed out of your Firebase Database (hint: you don't need this unless you're running millions of queries per minute and probably only for gaming logic and similar), you can dual-write your records (i.e. denormalize) as follows:

/foo/data/$id/... data goes here...
/foo/index/$id/true (just a boolean value)

To dual write, you would use the update command, and a write similar to the following (Android SDK sample):

public void addRecord(Map<String, Object> data) {
   DatabaseReference db = FirebaseDatabase.getInstance().getReference();

   // create a new record id (a key)
   String key = db.child("foo").push().getKey();

   // construct the update map
   Map<String, Object> dualUpdates = new HashMap<>();
   dualUpdates.put("/data/" + key, /* data here */);
   dualUpdates.put("/index/" + key, true);

   // save the new record and the index at the same time
   db.child("foo").updateChildren(dualUpdates);
}

Now to determine if a record exists, without actually downloading the data, I can simply query against /foo/index/$id and try DataSnapshot.exists(), at the cost of downloading a single boolean.

Thanks for the answer! What solution do you suggest for maximum security of the data? I'm alright with my current setup (because it works) but the read = true is slightly unnerving because then anyone can see all my users' usernames and their UIDs, correct?

The best security would be to set read: true on the specific records, but not allow read of the parent path. That would prevent anyone from browsing the uids; they would need to know a user's id to look them up. I don't know what your use case is here, so I can't really offer any specific advice. But you may want to read up on the XY problem, as you may be asking the wrong questions.

android - Firebase check if a value exists without reading a snapshot ...

android firebase firebase-database firebase-authentication firebase-security
Rectangle 27 2

Deleting a user doesn't revoke existing tokens for that user. See Firebase authentication not revoked when user deleted?. If you're using one of the standard identity providers, this means that the users may still be able to access the data for an hour after you delete the account.

There is no API for you code to check whether a given uid still exists. And even if such an API existed, it wouldn't help in this case, since a malicious user could just bypass that check and call the API directly.

A simple way to deal with this scenario is to keep a whitelist of allowed or blacklist of disallowed users in your database. For a blacklist, you'd keep a top-level (world readable, admin only writeable) list of banned/deleted users:

banned
  uid12345: true

And then in your security rules, you check and disallow access for banned users. E.g.:

"posts": {
  ".read": "auth != null && !root.child('banned').child(auth.uid).exists()"
}

How to set Firebase Database rules? How to prevent .write from deleted...

firebase firebase-database firebase-authentication firebase-security
Rectangle 27 2

Deleting a user doesn't revoke existing tokens for that user. See Firebase authentication not revoked when user deleted?. If you're using one of the standard identity providers, this means that the users may still be able to access the data for an hour after you delete the account.

There is no API for you code to check whether a given uid still exists. And even if such an API existed, it wouldn't help in this case, since a malicious user could just bypass that check and call the API directly.

A simple way to deal with this scenario is to keep a whitelist of allowed or blacklist of disallowed users in your database. For a blacklist, you'd keep a top-level (world readable, admin only writeable) list of banned/deleted users:

banned
  uid12345: true

And then in your security rules, you check and disallow access for banned users. E.g.:

"posts": {
  ".read": "auth != null && !root.child('banned').child(auth.uid).exists()"
}

How to set Firebase Database rules? How to prevent .write from deleted...

firebase firebase-database firebase-authentication firebase-security