Structure Your Database

Before you begin

Before you can useRealtime Database,you need to:

  • Register your Unity project and configure it to use Firebase.

    • If your Unity project already uses Firebase, then it's alreadyregistered and configured for Firebase.

    • If you don't have a Unity project, you can download asample app.

  • Add theFirebaseUnity SDK (specifically,FirebaseDatabase.unitypackage) toyour Unity project.

Find detailed instructions for these initial setup tasks inAdd Firebase to your Unity project.

Note that adding Firebase to your Unity project involves tasks both in theFirebase console and in your open Unity project(for example, you download Firebase config files from the console, then movethem into your Unity project).

Structuring Data

This guide covers some of the key concepts in data architecture and bestpractices for structuring the JSON data in yourFirebase Realtime Database.

Building a properly structured database requires quite a bit of forethought.Most importantly, you need to plan for how data is going to be saved andlater retrieved to make that process as easy as possible.

How data is structured: it's a JSON tree

AllFirebase Realtime Database data is stored as JSON objects. You can think ofthe database as a cloud-hosted JSON tree. Unlike a SQL database, there are notables or records. When you add data to the JSON tree, it becomes a node in theexisting JSON structure with an associated key. You can provide your own keys,such as user IDs or semantic names, or they can be provided for you usingthePush() method.

Note: If you create your own keys, they must be UTF-8 encoded, can be a maximumof 768 bytes, and cannot contain.,$,#,[,],/, or ASCII controlcharacters 0-31 or 127. You cannot use ASCII control characters in the valuesthemselves, either.

For example, consider a chat application that allows users to store a basicprofile and contact list. A typical user profile is located at a path, such as/users/$uid. The useralovelace might have a database entry thatlooks something like this:

{"users":{"alovelace":{"name":"Ada Lovelace","contacts":{"ghopper":true},},"ghopper":{"..."},"eclarke":{"..."}}}

Although the database uses a JSON tree, data stored in the database can berepresented as certain native types that correspond to available JSON typesto help you write more maintainable code.

Best practices for data structure

Avoid nesting data

Because theFirebase Realtime Database allows nesting data up to 32 levels deep,you might be tempted to think that this should be the default structure.However, when you fetch data at a location in your database, you also retrieveall of its child nodes. In addition, when you grant someone read or write accessat a node in your database, you also grant them access to all data under thatnode. Therefore, in practice, it's best to keep your data structure as flatas possible.

For an example of why nested data is bad, consider the followingmultiply-nested structure:

{// This is a poorly nested data architecture, because iterating the children// of the "chats" node to get a list of conversation titles requires// potentially downloading hundreds of megabytes of messages"chats":{"one":{"title":"Historical Tech Pioneers","messages":{"m1":{"sender":"ghopper","message":"Relay malfunction found. Cause: moth."},"m2":{...},// a very long list of messages}},"two":{"..."}}}

With this nested design, iterating through the data becomes problematic. Forexample, listing the titles of chat conversations requires the entirechatstree, including all members and messages, to be downloaded to the client.

Flatten data structures

If the data is instead split into separate paths, also called denormalization,it can be efficiently downloaded in separate calls, as it is needed. Considerthis flattened structure:

{// Chats contains only meta info about each conversation// stored under the chats's unique ID"chats":{"one":{"title":"Historical Tech Pioneers","lastMessage":"ghopper: Relay malfunction found. Cause: moth.","timestamp":1459361875666},"two":{"..."},"three":{"..."}},// Conversation members are easily accessible// and stored by chat conversation ID"members":{// we'll talk about indices like this below"one":{"ghopper":true,"alovelace":true,"eclarke":true},"two":{"..."},"three":{"..."}},// Messages are separate from data we may want to iterate quickly// but still easily paginated and queried, and organized by chat// conversation ID"messages":{"one":{"m1":{"name":"eclarke","message":"The relay seems to be malfunctioning.","timestamp":1459361875337},"m2":{"..."},"m3":{"..."}},"two":{"..."},"three":{"..."}}}

It's now possible to iterate through the list of rooms by downloading only afew bytes per conversation, quickly fetching metadata for listing or displayingrooms in a UI. Messages can be fetched separately and displayed as they arrive,allowing the UI to stay responsive and fast.

Create data that scales

When building apps, it's often better to download a subset of a list.This is particularly common if the list contains thousands of records.When this relationship is static and one-directional, you can simply nest thechild objects under the parent.

Sometimes, this relationship is more dynamic, or it may be necessary todenormalize this data. Many times you can denormalize the data by using a queryto retrieve a subset of the data, as discussed inRetrieve Data.

But even this may be insufficient. Consider, for example, a two-way relationshipbetween users and groups. Users can belong to a group, and groups comprise alist of users. When it comes time to decide which groups a user belongs to,things get complicated.

What's needed is an elegant way to list the groups a user belongs to andfetch only data for those groups. Anindex of groups can help agreat deal here:

// An index to track Ada's memberships{"users":{"alovelace":{"name":"Ada Lovelace",// Index Ada's groups in her profile"groups":{// the value here doesn't matter, just that the key exists"techpioneers":true,"womentechmakers":true}},// ...},"groups":{"techpioneers":{"name":"Historical Tech Pioneers","members":{"alovelace":true,"ghopper":true,"eclarke":true}},// ...}}

You might notice that this duplicates some data by storing the relationshipunder both Ada's record and under the group. Nowalovelace is indexed under agroup, andtechpioneers is listed in Ada's profile. So to delete Adafrom the group, it has to be updated in two places.

This is a necessary redundancy for two-way relationships. It allows you toquickly and efficiently fetch Ada's memberships, even when the list of users orgroups scales into the millions or whenRealtime Database security rulesprevent access to some of the records.

This approach, inverting the data by listing the IDs as keys and setting thevalue to true, makes checking for a key as simple as reading/users/$uid/groups/$group_id and checking if it isnull. The index is fasterand a good deal more efficient than querying or scanning the data.

Next Steps

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2025-12-11 UTC.