Firebase’s Realtime Database is stored “in the cloud” — which simply means it's hosted on a remote datacenter, like Google’s large server farms in the central USA. These powerful servers handle many users and applications at once.
When your code sends a request to read or write data, there are two possible ways to handle the delay (called latency):
Synchronous (Blocking):
Your code waits for the request to finish before continuing.
❌ This can make the app appear frozen or laggy while waiting for a response.
Asynchronous (Non-blocking):
Your code sends the request and keeps running. When the result comes back, a special function is called to handle it.
✅ This keeps the app responsive and smooth.
Because Firebase is hosted remotely, asynchronous communication is essential.
Firebase Library Setup:
Just like you loaded libraries like p5.js and p5Play to create games, your code must first load the Firebase modules it needs (like firebase-app or firebase-database).
Sending a Request:
When your code issues a Firebase command (e.g., to read data), it.
Calls a Firebase function already loaded in memory.
Passes details like what data to get and which of your functions to run when it’s done.
Request Sent to Server:
The Firebase code formats the request and sends it over the internet to a Google server.
Immediate Code Continuation:
Your code doesn’t wait. It keeps running immediately, even though the request is still traveling across the internet.
Receiving a Response:
When the Firebase server finishes the request:
It sends the result back to your browser (success or error).
The Firebase code in the browser matches the response with the original request.
It runs the correct function you provided in step (2):
✅ Success will pass you the result and run the function
❌ Fail will pass you the error and run the function
There are a number of ways you can get Firebase Realtime Database to notify your code of the result of your request. We will be using JavaScript promises.
promises More on w3schools
In JavaScript, a Promise represents a value that may not be available yet, but will be at some point in the future. It's called a "promise" because it promises to either:
✅ resolve (i.e., successfully return data), or
❌ reject (i.e., return an error),
With Firebase Realtime Database, when you do something like a read or write, Firebase returns a promise to you right away — kind of like saying:
“I’m working on it! I promise I’ll let you know when I’m done.”
Then for:
Firebase resolves the promise and your .then() function is triggered to handle a successful result.
If there's an actual problem (e.g., permission denied, network failure), Firebase rejects the Promise, and your .catch() function is triggered to handle the error. The .catch() function receives an error object with:
error.code: a short string identifying the type of error
→ Example: "auth/wrong-password"
error.message: a longer, readable explanation of the error
Create a Firebase Realtime Database and add, modify & delete data via the console.
See the steps below
add firebase data via the console
Create a path
Add keys
add values
Use the Git Codespace given to you by your teacher for this topic it is based off this repository
Tailor the module comments
Create a function comment template.
The Git Codespace contains:
index.html & style.css
main.mjs - which is the main entry point for index.html
fb_io.mjs - which is the module to contain all your firebase code
Look at the ES6 modules webpage for information on ES6 modules and:
entry points
import and export
Both modules contain a module comment containing:
The full name of the JS module
A brief description of what the module does
Who wrote it and when ( Term1 2025) this requires updating
Plus a coloured console.log stating the modules name, using blue text on a white background
EG:
/**************************************************************/
// main.mjs
// Written by Mr Bob, Term 1 2025
// Main entry for index.html
/**************************************************************/
const COL_C = 'white'; These two const are part of the coloured
const COL_B = '#CD7F32'; console.log for functions scheme - see below.
console.log('%c main.mjs',
'color: blue; background-color: white;');
Create a template comment block for a function. It should:
Stand out when another programmer scrolls through your code.
Be comprehensive enough so that another programmer can look at your function comments and know how to call the function without looking at the code.
A comment block separator line long enough to stand out but short enough to not wrap around in an IDE.
It should contain:
The name of the function - including the input parameter definition. Example of function with no input parameters; createPlayer() example of function with input parameters; createAliens(numberOfAliens)
A brief description of what the function does
State what code calls the function - you will find this a very useful practice for next term when we develop complex code to interact with firebase. EG; Called by html START button or Called by setup()
A full description of any input parameters - if there are none say so, EG; n/a
A full description of any return data - if there is none say so, EG; n/a
NOTES:
The comment block should not be too wide, that is you want to be able to see it without needing to scroll right. 80 characters is the standard.
Comments do not need to be written in correct English, allowing you to write succinct but clear comments.
The comment block for P5.js functions preload(), setup() & draw() only need to stand out for scrolling purposes and include the function name as you can assume another programmer looking at your code understands how these functions are called, etc.
You might like to make debugging easier by implementing coloured console.logs for functions, where each module uses a different colour scheme. See example below.
EG:
/******************************************************/
// fb_login()
// Called by html LOGIN button
// Login to Firebase via Google authentication
// Input: n/a
// Return: n/a
/******************************************************/
function fb_login() {
console.log('%c fb_login(): ',
'color: ' + COL_C + '; background-color: ' + COL_B + ';');
You have 9 html buttons labelled to match the topics below.
Each topic below forms a task.
Use the template your created in TASK 02 and create a function for each topic and call it via the associated html button.
03. initialise firebase - using CDN
04. authenticate to firebase - via signInWithPopup
05. detect authentication changes - via onAuthStateChanged
07. write a record to firebase - via set
08. read a specific firebase record - via get
09. read all records in a path - via get
10. update a firebase record - via update
11. read sorted - via query + orderByChild + limitToFirst + get
12. secure your DB - via realtime firebase rules
Extensions
task 03. initialise firebase - using CDN
The following services to be imported (at the top of the file):
import { initializeApp }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
import { getDatabase }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To connect to YOUR firebase you need the configuration built when you created your Firebase Realtime Database. Go to firebase and:
select firebase console → Project Overview → Project Settings → General → scroll down to SDK setup and configuration → select CDN
You can connect to multiple Firebase Realtime Databases. In which case you would need to identify the individual database via the names you give the config and app definitions.
To make that point clear, the config, app and database names below are; FB_GAMECONFIG, FB_GAMEAPP & fb_gamedb
Pick sensible names.
The database (fb_gamedb) needs to have global scope, declare it at the top of the file
var fb_gamedb
The rest goes in fb_initialise()
const FB_GAMECONFIG = {
apiKey: "?????",
authDomain: "?????",
databaseURL: "?????",
etc
};
const FB_GAMEAPP = initializeApp(FB_GAMECONFIG);
fb_gamedb= getDatabase(FB_GAMEAPP);
console.info(fb_gamedb); //DIAG
NOTES:
You must initialise the connection to your Firebase Realtime Database before you can use any of the features.
If you load a new html page all your browser memory is lost, therefore you will need to re-initialise the connection to your database once the new html page is loaded.
task 04. authenticate to firebase - via signInWithPopup
Create and export the fb_authenticate() function in fb_io.mjs
Export the function
Import the function in main.mjs
add the function to the button in index.html
The following services to be imported:
import { getAuth, GoogleAuthProvider, signInWithPopup }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-auth.js";
const AUTH = getAuth();
const PROVIDER = new GoogleAuthProvider();
// The following makes Google ask the user to select the account
PROVIDER.setCustomParameters({
prompt: 'select_account'
});
signInWithPopup(AUTH, PROVIDER).then((result) => {
✅ Code for a successful authentication goes here
})
.catch((error) => {
❌ Code for an authentication error goes here
});
NOTES:
There are a number of providers of authentication services, and a number of ways to interface with the user.
We are using Google as the provider and request that it uses a pop-up to interface with the user.
result.user is an object containing the authenticated user's information:
displayName
photoURL
uid (The user's ID is unique to the Firebase project)
Once you have successfully authenticated, console.log the user's email, and uid
In the error code you can console.log the error parameter to see what went wrong: console.log(error);
task 05. detect authentication changes - via onAuthStateChanged
The following services to be imported:
import { getAuth, onAuthStateChanged }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-auth.js";
const AUTH = getAuth();
onAuthStateChanged(AUTH, (user) => {
if (user) {
✅ Code for user logged in goes here
} else {
✅ Code for user logged out goes here
}
}, (error) => {
❌ Code for an onAuthStateChanged error goes here
});
Use the status message to display the authentication state.
NOTES:
If you load a new html page all your browser memory is lost, therefore you will need to:
re-initialise the connection to your database once the new html page is loaded
AND then issue onAuthStateChanged to re-authenticate the user.
task 06. logout - via signOut
The following services to be imported:
import { signOut, getAuth }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-auth.js";
const AUTH = getAuth();
signOut(AUTH).then(() => {
✅ Code for a successful logout goes here
})
.catch((error) => {
❌ Code for a logout error goes here
});
Use the status message to display the the signOut status.
task 07. write a record to firebase - via set
The following services to be imported:
import { ref, set }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
Where in the database is the data to be written.
The data to be written.
const dbReference= ref(what-DB, where-to-write-to);
set(dbReference, data-to-write).then(() => {
✅ Code for a successful write goes here
}).catch((error) => {
❌ Code for a write error goes here
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: fb_gamedb
where-to-write-to – the path and key
data-to-write – data must be an object.
Use the status message to display the a confirmation message.
NOTES:
This overwrites the entire record at the location specified, see update to modify field(s) within a record.
If you get a Permission Error you will need to check your firebase rules
For example, in this database:
The path and key (where-to-write-to) is Games/Pong/Score/UID
The data (data-to-write ) is {HighScore: 3, Name: 'Bob'}
The following services to be imported:
import { ref, get}
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
Where in the database is the data to be read from.
const dbReference= ref(what-DB, where-to-read-from);
get(dbReference).then((snapshot) => {
var fb_data = snapshot.val();
if (fb_data != null) {
✅ Code for a successful read goes here
} else {
✅ Code for no record found goes here
}
}).catch((error) => {
❌ Code for a read error goes here
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: FB_GAMECONFIG
where-to-read-from – the path and key
Use the status message to display the information read from firebase.
NOTES:
✅ Success
In Firebase, a successful read means the request did not encounter an error — even if the data doesn't exist.
So, if you try to read a record that doesn't exist, Firebase still considers it a successful request.
Firebase resolves the promise and your .then() function is triggered, passing it an object (programmers usually name it snapshot as it's a snap shot of the database).
The snapshot object:
Contains methods to work with the data.
Commonly used method: snapshot.val();
→ This returns the data if it exists, or null if the record doesn't exist.
For example, in this database:
The path and key (where-to-read-from) could be Games/Pong/Score/UID/Name
It will return the value "Bob"
task 09. read all records in a path - via get
The following services to be imported:
import { ref, get }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
Where in the database is the data to be read from.
const dbReference= ref(what-DB, where-to-read-from);
get(dbReference).then((snapshot) => {
var fb_data = snapshot.val();
if (fb_data != null) {
✅ Code for a successful read all goes here
} else {
✅ Code for no record found goes here
}
}).catch((error) => {
❌ Code for a read all error goes here
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: FB_GAMECONFIG
where-to-read-from – the path ONLY
Use the status message to display the information read from firebase.
NOTES:
The firebase command to read all records in a path is the same as to read a record.
The difference is in specifying where in the database the data is to be read f rom. With a:
read record you specify a path and a key
read all records in a path you specify just the path.
Processing the data returned is different from read a record.
For example, in this database:
The path (where-to-read-from) could be Games/Pong/Score/UID
It will return the object {HighScore: 3, Name: "Bob"}
task 10. update a firebase record - via update
The following services to be imported:
import { ref, update }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
Where in the database is the data to be written.
The data to be written.
const dbReference= ref(what-DB, where-to-write-to);
update(dbReference, data-to-write ).then(() => {
✅ Code for a successful update goes here
}).catch((error) => {
❌ Code for a update error goes here
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: FB_GAMECONFIG
where-to-write-to – the path and key
data-to-write – data must be an object.
Use the status message to display a confirmation message.
NOTES:
This ONLY modifies the specific field(s) within the record, see the set command to overwrite the entire record.
Supplying one key:value paring will update the corresponding field in the database
Make sure you console.log the error parameter to see what went wrong
For example, in this database:
The path (where-to-write-to) could be Games/Pong/Score/UID
and data-to-write could be {Name: "Bruno"}
This will change the Name from Bob to Bruno
task 11. read sorted - via query + orderByChild + limitToFirst + get
The following services to be imported:
import { ref, query, orderByChild, limitToFirst }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
Where in the database is the data to be written.
The data to be written.
const dbReference= query(ref(what-DB, where-to-read-from), orderByChild(sortkey), limitToFirst(number-to-read));
get(dbReference).then((snapshot) => {
var fb_data = snapshot.val();
if (fb_data != null) {
✅ Code for a successful sorted read goes here
} else {
✅ Code for no record found goes here
}
}).catch((error) => {
❌ Code for a sorted read error goes here
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: FB_GAMECONFIG
where-to-read-from – the path and key
sortkey – the sort key
number-to-read – the number of records to provide
Use the status message to display the sorted data.
NOTES:
Records are sorted in ascending order limited to the first n records.
task 12. secure your DB - via realtime firebase rules
"first_name": {".validate": "newData.isString()"}
"first_name": {
".validate": "newData.isString() &&
newData.val().length < 100"
}
"age": {".validate":
"newData.isNumber() && newData.val() >= 18 && newData.val() <= 65"
}
"angle": {".validate": "newData.isNumber() &&
(newData.val() == 0 ||
newData.val() == 90 ||
newData.val() == 180 ||
newData.val() == 270)"
}
first_name must be a string
first_name must be a string and its length must not exceed 100 characters
age must be a number between 18 and 65
angle must be a number and can only be one of the values specified (0,90,180 or 270).
ext (a). listening for changes to firebase - via onValue
The following services to be imported:
import { ref, onValue }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
What part of the database is to be monitored and read from.
const dbReference = ref(what-DB, where-to-monitor-&-read-from);
onValue(dbReference, (snapshot) => {
var fb_data = snapshot.val();
if (fb_data != null) {
✅ Code for a successful read goes here
} else {
✅ Code for no record found goes here
}
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: FB_GAMECONFIG
where-to-monitor-&-read-from – the path, key & field to monitor & read
NOTES:
Supplying just the path results in the whole path being monitored, so any changes to the path triggers the listener.
Supplying both the path & key results in the record being monitored, so any changes to the record triggers the listener.
Supplying both the path & key plus the field results in just the field being monitored, so only changes to the field triggers the listener.
ext (b). delete a record - via remove
The following service to be imported:
import { ref, remove }
from "https://www.gstatic.com/firebasejs/9.6.1/firebase-database.js";
To know what database to work with.
Where in the database is the data to be deleted.
const dbReference= ref(what-DB, what-to-delete);
remove(dbReference).then(() => {
✅ Code for a successful delete goes here
}).catch((error) => {
❌ Code for a delete error goes here
});
what-DB – reference to your database was stored in a constant during firebase initialization. EG: FB_GAMECONFIG
what-to-delete – the path and key of the record to delete