3
votes

In the doc, it says :

Using the get() and exists() functions, your security rules can evaluate incoming requests against other documents in the database.

Thats all right to me, and the example makes sense to me:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before allowing any writes to the 'cities' collection
      allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid))

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    }
  }
}

but an then it says

For writes, you can use the getAfter() function to access the state of a document after a transaction or batch of writes completes but before the transaction or batch commits.

I might still don't fully understand the concept. My questions are:

  1. Why is it specifically have to use getAfter() for the transaction or batch write, Can we just use get()?
  2. If you have to use getAfter() for transaction or batch write, does that mean you still need get() for normal write? how do they exist at the same time?

Thanks.

1

1 Answers

7
votes

First, bear in mind that that security rules for writes kick in before anything in the database has been changed by that write. That's how the security rules are able to safely and efficiently reject access, without having to roll back any writes that already happened.

The documentation you're citing suggests that getAfter is useful to examine the contents of the database after the entire transaction's state would be recorded (in a sort of "staging" environment in memory), but before the transaction actually changes the database, visible to everyone. This is different than get, because get only looks at the actual contents of the database, before the transaction is finally committed. In short, getAfter uses then entire staged write of the entire transaction or batch, while get uses the actual existing contents of the database.

You are by no means obliged to use getAfter, if get works just fine for your case.

getAfter is useful when you need to examine other documents that may have been changed in the transaction or batch, and still have a chance to reject the entire transaction or batch by failing the rule. So, for example, if two documents being written in a single transaction must have some field value in common in order to be consistent, you need to use getAfter to verify the equality between the two. get wouldn't be helpful here, because it doesn't know anything about the other document in the transaction that hadn't been written yet.

On the other hand, if your rule needs to check if a document in the transaction has not changed an a field in an existing document (that is not the current document being checked), get would be necessary to fetch that value instead, before it's written by the transaction.