In this tutorial we will build a very basic to-do web app. You can think of this tutorial as a demonstration of the core functionality of Userbase in the simplest way possible. We are going to focus solely on building a functional web app. Making things pretty is left as an exercise to the reader.
The entire web app we'll be building will fit in a single static HTML file of 182 lines. You can also see a live demo of the final result.
Let's get going. Open a new file in your favorite code editor.
And add some boilerplate HTML.
Now, open this file in a web browser of your choosing. At this point all you'll see is a blank page. As we add functionality throughout the tutorial, you can refresh this page to see the changes.
To complete this tutorial, you'll need to create a Userbase admin account. Upon creation, a default application named "Starter App" will be created. Take note of the App ID because we'll need it very soon.
We're going to load the Userbase SDK from a CDN with a <script>
tag in the head of our page.
The Userbase SDK will now be accessible via the userbase
variable. This will be our only dependency.
Before doing anything with the Userbase SDK, we need to let it know our App ID. Simply replace 'YOUR_APP_ID'
with the App ID you received when you created your admin account.
Before our users can start creating to-dos, we need to give them a way to create an account with our app.
First, let's add a sign up form.
Then, let's add the code to handle the form submission.
Now, whenever someone submits the form, the handleSignUp
function will be called. This gets the values of the username and password inputs and calls userbase.signUp({ username, password, rememberMe: 'none' })
to create a new user account with Userbase. We are specifying rememberMe: 'none'
because we won't implement automatic login until the end of the guide.
Go ahead and reload the page in your browser. Enter a username and password in the form and submit. You should get an alert saying that you signed up. And if you go to your Userbase admin account, you should also see the new user under your app.
Now try signing up for another account using the same username and you'll see an error message displayed under the form.
We'll come back to this function in a bit to make it do something more interesting.
Now that our users can create accounts, let's give them the ability to login.
First, let's add a "Login" form to the page above our "Create Account" form.
Then, let's add the code to handle the form submission.
And finally, let's bind our login form with our login handler.
You'll notice that this looks very similar to the sign up code from before. The handleLogin
function gets the values of the username and password inputs, and calls userbase.signIn({ username, password, rememberMe: 'none' })
. This will attempt to sign in the user, handling a success with an alert and a failure by displaying the error.
Reload the page and you should see our new login form. Enter the username and password you used to create an account in the previous step, and submit the form. You should get an alert saying that you signed in.
Try submitting the form again with incorrect credentials and you'll see an error message displayed under the form.
After a user signs in, we'll want to hide the authentication forms and display their to-do list. First, let's add a container for the to-do list under the authentication forms.
Then, let's make this view hidden by default, and add a function to display it.
Now that we have a function to show a view for signed in users, let's change
handleLogin
to call this function instead of showing an alert.
And we do the same thing for handleSignUp
.
Reload the page and login again using your username and password. You should see the authentication view disappear and your username show up along with the to-do list heading.
Every time a user signs in, we need to establish a connection with the database that will hold the user's to-dos.
First, let's add a couple of elements for showing a loading indicator and error messages.
Then, let's change showTodos
to open a new database connection.
We changed showTodos
to make a call to userbase.openDatabase({ databaseName: 'todos', changeHandler })
. After the 'todos'
database is opened, our callback
function changeHandler
will be called whenever data changes in the database. When the Promise is resolved, the database is ready for use and we hide the loading indicator.
Reload the page and sign in again. You'll see "Loading to-dos..." while the connection to the database is getting established, followed by "Empty", indicating there are currently no to-dos in the database.
If the database has items in it, we'll want to render those under our to-do list.
Let's implement that in changeHandler
.
Now, let's add a form for creating new to-dos.
Then, let's add the code to handle the form submission.
In addTodoHandler
we get the to-do text from the input, and then call userbase.insertItem
with the database name and the object we want the persist. This
will return a Promise that resolves when the data is successfully persisted to
the database.
Reload the page and add some to-dos. Then, reload the page again and the to-dos should automatically appear after you login. These to-dos have been successfully persisted in the end-to-end encrypted Userbase database.
Now, let's add a checkbox to allow to-dos to be marked as completed.
Reload the page and complete some to-dos. Their state should persist even after you reload the page and login again.
And finally, let's create a button for deleting a to-do.
And now let's append the delete button to the to-do element.
Reload the page and delete some to-dos. They should no longer show up even after you reload the page and login again.
Before we wrap up, let's add two final pieces of account functionality: user logout and automatic login for returning users.
First, let's add a logout button along with a container for error messages.
Then, let's add the code to handle the logout.
The handleLogout
function calls userbase.signOut
which sends a request to end the user's session. A Promise is returned that resolves when the user is signed out, in which case we hide the to-do view and show the authentication view.
Let's modify our app to automatically sign in a returning user when the page loads. First, we'll add a loading indicator that will show while the app is trying to automatically sign in the user.
Then, let's add the following to our userbase.init
call.
We are now hiding the authentication view by default so that it will only show if an existing session can't be resumed.
The userbase.init
function returns a Promise that resolves when the SDK has determined if it can reuse the previous session. If so, the user gets automatically logged in, and the session.user
object gets set. If there was no previous session, or the session cannot be resumed, the session.user
object will not be set. If userbase.init
fails, we'll just send the user to the sign in page regardless of the reason.
Next, we want to tell the SDK to persist the session even after we refresh the page or close the browser's window. To do that, we need to set the rememberMe
parameter to either 'local'
(persists after the window gets closed) or 'session'
(persists only when the page gets refreshed) when calling userbase.signIn
and userbase.signUp
. Let's set ours to 'local'
.
Now, if we sign in and close the page, we will get signed in automatically next time we open it, without having to re-enter our username and password.
And that was it! A fully working (but ugly) web app in just 182 lines of code, including markup and comments. If you have any questions, or there's anything we can do to help you with your web app, please get in touch. Thank you!