Using Firebase as server-side backend for advanced Verge3D apps

Sometimes you need to implement quite sophisticated server-side logic to power your Verge3D applications. For example:
- Authenticated access. The system allows creating user accounts and handles logins (via email/password, social networks, phone etc.)
- Persistent per-user storage. The users keep their data in the system, for example to preserve product configurations for later use.
- Multi-user access. 3D interactive supports modifications made by different users (sometimes simultaneously!).
- Integration. The user places an inquiry on a company website and the system forwards it to some CRM or ERP system for further processing.
- Gamification. A browser game or a game-like experience handles user's scores/levels/communications.
- Connectivity. The system sends messages/emails to specified managers/users when some event is triggered in your app.
- Storing stats. The system collects info about user activity, e.g. detects preferred products for e-commerce app or keeps progress for e-learning apps.
- Implementing some crazy idea not mentioned above!
Basically, these are the cases when you typically hire a qualified server-side coder (or a team of coders) to do the job along with the system administrator to power-up your server configurations.
Thankfully, there are services that simplify developing server-side logic and fit quite well with the Verge3D paradigm, that is "do not code — make everything with Puzzles!". Such systems are known as Backend-as-a-Service (BaaS), with Google's Firebase as the most prominent example.
Why Firebase?
Firebase is an excellent back-end choice for Verge3D apps as it offers the following features out of the box:
- Authentication and access control — you can implement signup/login with just a few puzzles
- Simplistic but really powerful database called Firestore — again, can be easily accessed with puzzles
- Secure file storage — if you need to store files/images/videos/PDFs
- Serverless logic — it allows running code snippets automatically in response to user events or network requests
- Optional hosting — since there is no need to store apps on Google servers, you are free to choose Verge3D Network or your own hosting solution
- Compatibility with App Manager — you can use Firebase features right on your Verge3D development machine without uploading anything to a remote server
- Convenient tools for analytics and performance monitoring — very handy and easy to implement
Starting up
In this article we discuss steps needed for running a full-featured Verge3D application powered by a Firebase backend. As an example we use a simplistic 3D configurator with a single model. The model will change its color upon clicking, and this configuration can be saved on the remote server. The saved configurations can be retrieved. One of them can be selected and applied to the model at any time.
The most complex part of the application is handling user accounts. We allow creating new users with email/passwords, logging these users to the system, and displaying the status to the user.
You can try out the resulting app here. We made its source files freely available, so you can make your own projects with minimal efforts.
Creating a Firebase project
To create a new project use the Firebase console (you need a Google account for that):
and add/register a new web app as part of it:
Read more on creating web-based projects in the Firebase docs.
On this stage you obtain a pair of Firebase identifiers for your project and app. The actual production will happen in the Verge3D App Manager.
Creating a Verge3D app
Web-based nature of Verge3D presents a very exciting opportunity. You don't need to create a web application specifically for Firebase because you already created one with Verge3D. For example, to design a chair configurator like that:
... all you have to create a configurable chair model which can be rotated/scaled and a bunch of UI buttons in the top-left corner of the app.
The logic for the app is as follows:
- The user creates a new account using email/password and signs into it
- The user clicks on the chair to select a random color for the upholstery
- If the color looks good, it can be saved into the personal dashboard under some name (e.g. "Chair for my lounge")
- The user can restore the saved color at any time: immediately, or after reloading the page, or from some other device/browser, etc.
Let's explain Puzzles for randomizing upholstery color right away:
Here we use the when clicked puzzle to register mouse/touch events. Upon such event, a random color (from 0-1 range) is applied to the model thanks to set color.
Creating HTML for the app
The configurator app is operated using 5 (4 visible + 1 hidden) buttons, located in the top-left corner of the app:
- Signup button — used to create a new account
- Login/Logout buttons — used to login/logout. These are 2 distinct buttons, but only one is visible.
- Save button — displays dialog to save chair configuration
- Load button — displays dialog to load named configuration from the list
Also there is an email text field. It displays "Please signup / login" text if user is not yet logged in, or user email otherwise.
Let's discuss everything in detail.
Buttons and text field
We use add HTML element, set prop, and set style Puzzles to create all 5 buttons and a text field:
These controls are built from the <div> HTML elements arranged into two rows (first-row-buttons and second-row-buttons).
The buttons have the following IDs:
signup-buttonlogin-buttonlogout-buttonsave-buttonload-button
To make the logic more compact, we create a new CSS class .button which will be used to assign styles for all buttons at once:
The text field login-label is used only once, so we assign its style right after creating the corresponding <div> element.
Dialogs
The Puzzles for these dialogs look complicated but the idea is the same. As we did with buttons, we use the same 3 puzzles (add HTML element, set prop, and set style) to compose dialog windows and their content.
HTML standard offers modern <dialog> element to simplify building dialogs. Inside we place a web form (<form>) which represents a collection of <div>, <input>, <button>, and <select> elements. The form elements are positioned using the grid layout — hence we use display: grid in the corresponding set style puzzles.
Starting with Verge3D 4.13, you can use the HTML Dialogs library to speed up creating conventional dialog windows.
Signup and login forms
The login-dialog contains two <input> elements and two <button> elements. It is used for both signup:
and login:
The Puzzles that create it:
OK and error messages
The ok-dialog contains 1-2 text lines (rendered as <div> elements) and the "OK" <button> element. It is used to display information and errors to the user:
The Puzzles that create it:
Saving configurations
The save-cancel-dialog contains an <input> element and two <button> elements. It is used to display the dialog for saving chair configurations to the remote database:
The Puzzles that create it:
Loading configurations
The load-cancel-dialog contains a <select> element and two <button> elements. It is used to display the dialog for loading chair configurations obtained from the remote database:
The Puzzles that create it:
Loading Firebase modules
To interact with Firebase you need to import several external libraries into your app:
https://www.gstatic.com/firebasejs/12.9.0/firebase-app-compat.js— core Firebase APIhttps://www.gstatic.com/firebasejs/12.9.0/firebase-auth-compat.js— API for implementing authenticationhttps://www.gstatic.com/firebasejs/12.9.0/firebase-firestore-compat.js— API for database access (Firestore)
External libraries are loaded from the init tab with the load library puzzle:
Once loaded, the corresponding APIs will be available on the rest of Puzzle tabs via the firebase namespace (see below).
Firebase bindings
Firebase APIs are made for JavaScript (or other languages), not Puzzles. To convert the corresponding method calls from JavaScript to Puzzles you need to implement several bindings — in other words, connectors between Puzzles and code:
For our configurator app we use the following methods:
initializeApp— initialize a Firebase appcreateUserWithEmailAndPassword— create a new user with email and passwordsignInWithEmailAndPassword— sign-in with email and passwordonAuthStateChanged— handle authentication status changessignOut— sign-out from the accountsetUserDoc— set a document in the Firestore databasegetUserDoc— retrieve a document from the Firestore database
Implementing these in your own Firebase app might be cumbersome, so you better copy-paste them from the freely available project sources.
Promises
Many Firebase methods do not return their results immediately. Instead they return special promise values which then should be waited for (resolved) to obtain the corresponding method results. Thankfully, there are special wait promise and promise value puzzles which will help you to manage promise values in a visual way. We use these puzzles extensively in our chair configurator app.
App initialization
Once you create a Firebase app, it will provide the configuration which looks as follows:
const firebaseConfig = {
apiKey: "AIzaSyB5YOputCtfa437SFluX0EGnBP7i67qyYo",
authDomain: "test-7bdf5.firebaseapp.com",
projectId: "test-7bdf5",
storageBucket: "test-7bdf5.firebasestorage.app",
messagingSenderId: "741340162909",
appId: "1:741340162909:web:c1550f2ab43e2bdde896b4"
};
This configuration should be passed as a dictionary value into the initializeApp API method:
Implementing auth logic
We'll use the same dialog window for handling user signups and logins. This will reduce copy-pasting and make your code more robust.
Showing signup dialog
Here we register the click event listener for the "Signup" button with the on event of puzzle. Once the user clicks on that button, we set the dialog variant variable to "SIGNUP" (we'll use it later), set dialog text, set text for the dialog's submit button, and finally display the actual dialog with the call method - showModal puzzle.
Showing login dialog
Here we register the click event listener for the "Login" button with the on event of puzzle. Once the user clicks on that button, we set the dialog variant variable to "LOGIN" (we'll use it later), set dialog text, set text for the dialog's submit button, and finally display the actual dialog with the call method - showModal puzzle.
Processing signup
As we stated earlier, we use the same dialog for signup and login. The actual behavior is managed by the dialog variant variable.
Our common processing logic starts with the on event of puzzle used to handle the submit event of the dialog form. In the beginning we also change our cursor to a waiting symbol with the set style puzzle.
The SIGNUP part of the event collects the user credentials from the form and executes the createUserWithEmailAndPassword API method. This method returns a promise value that should be waited for.
What happens next depends on whether the sign-up process finishes with success or error. In both cases we set the corresponding message as the first text line of the ok-dialog dialog (ok-dialog-text). In case of error, we additionally extract the error code from the resolved promise to set an error message for the second line of the dialog (ok-dialog-text-2) and display it to the user (set style → display → block).
Finally, we return the mouse cursor back to normal, close our login dialog and open the ok dialog.
Processing login
This is the second half of our submit event handler:
This part is responsible for managing the LOGIN variant of our dialog. Similarly to the first part, it collects the user credentials, but this time executes the signInWithEmailAndPassword API method.
In this snippet we do not use the returned user information (except for handling errors), since the actual login status modification is processed by the onAuthStateChanged (see below).
Listening for auth state changes
Firebase offers a really convenient method called onAuthStateChanged.
It listens for changes to the user's sign-in state in real time calling the provided callback procedure. For example, it may be used to automatically restore the login session upon the app reloading.
The auth state changed callback procedure takes the user info object as its parameter and does the following:
- Checks if the
userobject is defined ornull. Defined means the user just successfully logged in, while thenullvalue means he just logged out - Updates the
login-labeltext field - Sets
logged uidvariable with the current user ID - Swaps the "Login" and "Logout" buttons (as we discussed earlier, only one of them can be visible at once)
- Makes the second row of buttons ("Save" and "Load") opaque or semi-transparent. Semi-transparent buttons appear disabled, so the user won't click them mistakenly if not logged in
Performing logout
This logic is simple — upon clicking on the Logout button it executes the signOut API method:
Implementing save/load logic
To save/restore configurations we use the remote database called Firestore. Firestore is a powerful, fast, yet really easy-to-use database that is available from any Firebase project (hence the similarity between the two names). This database stores documents which are very similar to Puzzles' dictionary values (do not confuse these with Word or Excel documents).
Documents are stored/retrieved to/from the database by their IDs (unique strings, e.g. "miJebfFBqNa4QCJWyeV1"). Also, the Firestore documents are grouped into collections (this is mandatory, each document must be an item of some collection, e.g. "users").
In our Chair configurator app we store configurations on a per-user basis. For document ID we use the user's unique ID obtained during authentication (it is stored in the logged uid global variable). The document looks as follows:
{
configurations: [
{
name: "First Configuration",
upholsteryColor: "#RRGGBB"
},
{
name: "Second Configuration",
upholsteryColor: "#RRGGBB"
}
]
}
To see how your database looks like, go to Project → Databases & Storage → Firestore in your Firebase console:
Don't worry if you make mistakes in your app. You can always cleanup the entire database and start from scratch.
Saving configurations
Having designed the database structure we can proceed to the actual Puzzles to store the user's configuration.
Let's explain this scenario step by step.
In the beginning we use the usual pair of puzzles on event of and call method to show the save-cancel-dialog dialog:
Then the user clicks on the "Save" button, the submit event is sent to the save-cancel-dialog-form form. In the event handler we do the following:
- Change cursor to a waiting symbol as communicating with the remote server takes time
- Prevent the form submission. For dialog-based forms this means the dialog won't be closed immediately.
- Retrieve the current configuration name
- Execute the
getUserDocmethod to retrieve the document associated with the user ID - Retrieve the
configurationskey (aka field) from the document and save it to the list represented by theconfigsvariable (remember, the document structure is represented by a dictionary) - If the configurations list is present, we try to find an existing item with the
nameequal to the saved name. If the configurations list is not present, we create an empty one. - If the saved name item is present in the list, we set the
upholsteryColorfor the corresponding item - If the saved name item is not present in the list, we create a new item (also a dictionary) and set its
nameandupholsteryColorkeys - The configurations list is ready, so we create a new document and execute the
setUserDocmethod to store it in the remote database - Finally, we return the cursor style back to normal (set style → cursor → default) and close the dialog
Loading configurations
Logic for loading configurations consists of two important snippets:
- When the user clicks on the "Load" button from the top-left corner of the app, we open the "Select Configuration" dialog, retrieve the configurations from the database, then populate the dialog's option selector
- When the user clicks on the "Load" button from the dialog (do not confuse it with the previous "Load" button), we assign the selected upholstery color on our Chair model
The first step is similar to how the getUserDoc method was used during the saving stage. The difference is that instead of updating the configs list we fill the dialog's <select> element with options.
Each option is represented by the <option> element with the value property set to the corresponding configuration name. The option's color is stored in the dataset property as a CSS color (e.g. #ff0000 for red)
On the second step we retrieve the active <option> element with the query selector puzzle, extract color from its dataset property, then apply the extracted color to the model with the set color puzzle.
Project sources
The source files of the Chair configurator project are freely available here. Just download the provided .zip archive and drag and drop it into the App Manager.
Inside you will find the following folders and files:
public— source files for the Verge3D application (made for better compatibility, you can store your app in the root folder as well)firebase.json— Firebase project configurationfirestore.rules— security rules for the Firestore databasefirestore.indexes.json— Firestore indexes (not used)
Deploying your own project
For deploying projects, the official Firebase instructions suggest using the firebase CLI utility which is very efficient, but require entering commands in the terminal. If you prefer a visual approach, perform some preparations via the Firebase console:
- Enable email/password authentication with Project → Security → Authentication → Get started
- Create a Firebase database with Project → Databases & Storage → Firestore → Create database (select all options by default)
- Append the Firestore security rules (e.g. by copying/pasting the contents of the
firestore.rulesfile) with Project → Databases & Storage → Firestore → Rules
After that, you can deploy the project to Verge3D Network or choose your own hosting solution. Here is the Verge3D Network link for you to check it out.
Next steps
Firebase comes with many advanced features. However, using them won't give you much trouble — Firebase is well-documented, so AI assistants will be exceptionally good at helping you out.