12

Naturally the users in my database have information that can be publicly accessible and other information only they should see. I'm considering two different ways to implement this.

Option 1: Have /users/$uid readable only by that user and have /users/$uid/profile be readable by anyone.

Option 2: Keep /users/$uid readable only by that user and have a /profiles/$uid that is public. This follows the recommendation for a flatter data structure, but I don't see how it is better in this case.

Jacob Phillips
  • 8,841
  • 3
  • 51
  • 66

1 Answers1

27

The easiest way to see why the "flatter" structure is better is to look at how you'd secure it and how you'd then implement functionality.

Your first structure is:

users: {
  uidOfJacob: {
    stackId: 884522,
    ssn: "999-99-9999",
    profile: {
      displayName: "Jacob Philips"
    }

  },
  uidOfPuf: {
    stackId: 209103,
    ssn: "999-99-9999",
    profile: {
      displayName: "Frank van Puffelen"
    }
  }
}

You'd secure it with:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth.uid == $uid",
        ".write": "auth.uid == $uid"
        "profile": {
          ".read": true
        }
      }
    }
  }
}

One of the main reasons for having public information is to be able to show a list of that information. In JavaScript:

ref.child('users').child(???).child('profile').on('child_added'...

This won't work, because what do we put in ???. Firebase operations need to be able to read the entire list from one location, and the user needs to have read permission on that entire location (not just to the individual child nodes).

If we structure the data to separate the public information from the private information, we get:

users: {
  uidOfJacob: {
    stackId: 884522,
    ssn: "999-99-9999",
    profile: {
      displayName: "Jacob Philips"
    }

  },
  uidOfPuf: {
    stackId: 209103,
    ssn: "999-99-9999",
    profile: {
      displayName: "Frank van Puffelen"
    }
  }
},
"profile": {
  uidOfJacob: {
    displayName: "Jacob Philips"
  },
  uidOfPuf: {
    displayName: "Frank van Puffelen"
  }
}

You'd secure it with:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth.uid == $uid",
        ".write": "auth.uid == $uid"
      }
    },
    "profiles": {
      ".read": true,
      "$uid": {
        ".write": "auth.uid == $uid"
      }
    }
  }
}

Now to get a list of the public user profiles, you'd do:

ref.child('profiles').on('child_added'...

This will work, because everyone has read permission on profiles.

Manube
  • 5,110
  • 3
  • 35
  • 59
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • My current use case is to look at profile information when the user id is already known, which is why I had a hard time seeing the differences. The more nested approach prevents getting a list of profiles, which will likely come in handy later. – Jacob Phillips Jul 29 '16 at 02:59
  • this basically mean that new user, after registration, need to write two times in database: 1. new entry for "users" node (uid with it's subnodes), 2. new entry for "profile" node (uid with displayName subnode)? @FrankVanPuffelen – Ewoks Feb 21 '17 at 09:50
  • 2
    @Ewoks Yes, that's correct. Frank's approach is common in NoSQL database design, wherein the read performance is improved at the expense of diminishing write performance. – Rax Weber Jul 29 '17 at 16:22