Cloud Functions
Using Cloud Functions for Firebase lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests.
If you need a function to perform work on the database when called, you could configure a HTTP trigger to do this. For example, the below Cloud Function reads the last stored number from the database (stored at /lastNumber
), adds 1 (one) to it, saves it back to the database and then returns the current value:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.nextNumber = functions.https.onRequest((req, res) => {
var ref = admin.database().ref('/lastNumber');
ref.transaction(function(current) {
return (current || 0) + 1;
}, function(error, committed, snapshot) {
var currentNumber = snapshot.val().toString();
console.log("Generated number: ", currentNumber);
res.status(200).send(currentNumber);
});
});
Once deployed to Cloud Functions, you can call this function simply by visiting the HTTPS endpoint for it (where <region>
is your region & <project-id>
is your project ID):
https://<region>-<project-id>.cloudfunctions.net/nextNumber
There are a whole host of other examples available on the Firebase Functions Samples repository which you can browse & use.
Realtime Database
The Firebase Realtime Database is known as NoSQL which means that there are no direct relationships or foreign key constraints and therefore it is suggested that you employ a process of denormalization when storing data.
The documentation goes into detail on the best practices for structuring your data and the most important thing is to avoid nesting data, and instead flatten data structures as much as possible to create data that scales.
The basic premise here is that you want to avoid downloading all data from the database when you only need to obtain a select number of items, but likewise have the ability to link items together, much like you can in a relational database. From the documentation on data fanout:
Consider, for example, a two-way relationship between users and groups. Users can belong to a group, and groups comprise a list 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 and fetch only data for those groups. An index of groups can help a great 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
}
},
...
}
}
Duplicating of data and using indexes like this is a suggested pattern for these types of databases. That's why there are functions available that allow you to perform multi-location writes in order to simplify the denormalization process.
For example in Android, with the above database structure, you could easily insert a new user (with username testuser
) and add them to the techpioneers
group in a single operation:
DatabaseReference database = FirebaseDatabase.getInstance().getReference();
HashMap<String, Object> data = new HashMap<>();
// Build the new data using location paths
data.put("/users/testuser/name", "Test User");
data.put("/users/testuser/groups/techpioneers", true)
data.put("/groups/techpioneers/members/testuser", true);
// Save this new data all at once
database.updateChildren(data);
Firebase have published a number of samples, examples and code labs to help you get started.