Do you love Slack? We do too. We’re happy to introduce the new Firebase Slack community to discuss all things Firebase!
Slack has been a great tool for community members to help one another. The Angular and iOS Slack communities have grown to over 4,000 strong each, and we hope we can do the same.
When you want to learn a foreign language, the best way is to surround yourself with those who speak it.
The Firebase Slack community is a place to learn from other Firebase developers. Such as Firebase GDEs (Google Developer Experts), active Github contributors to libraries like AngularFire, and even Firebase team members (I’ll be there!).
The Slack community is a place to discuss app ideas, data structure questions, feature requests, guidance on problems, and feedback about Firebase in general. Oh, and we love GIFs too.
Our goal is to gather a diverse group to represent the community. In doing so, we expect community members to treat each other kindly and respectfully. We have a complete Code of Conduct that can be viewed here.
To join, click here to go to the inviter website. Once you input your email, you’ll get a confirmation to join in your inbox. (Make sure to check for spam if you don’t see it).
We hope to see you there soon.
Get ready for a whole new series of screencasts. Today we're excited to announce the launch of our new YouTube series, Firecasts!
Firecasts is a hands on YouTube series for Firebase developers. Tune in each week and learn how to build realtime apps on Android, iOS, and the Web.
While the first episode is on Android, Firecasts is platform agnostic. We have episodes queued for iOS and the Web as well. Tune in each week for a new screencast. And, if you want to see something specifically covered...
Your input matters. If you want to see a topic covered, tweet at us using the #AskFirebase hashtag. We'll be answering those questions in future videos.
At Firebase, we believe the best way to learn is by doing. We're excited to hear what you want to learn.
You've likely already heard that Parse will be shutting down. It brings us no joy in seeing them close their doors. While Parse was a competitor, they were also a part of the developer tools community and ecosystem. We were both working towards a common goal: empowering developers to create great apps.
I’m writing this post to reassure current and future Firebase developers that we are working full steam ahead on realizing that goal.
Firebase joined Google fifteen months ago, and the experience has been fantastic. We’ve had a chance to work closely with many products inside Google Cloud and across the company with teams like Play. We're in the process of moving our account system to Google accounts so we can deeply integrate with Google's family of developer products.
Also, we’ve been busy:
These are just a few examples.
For those of you who have asked about migration from Parse Core to the Firebase Realtime Database: there isn’t currently an easy migration path. The data models and APIs are quite different. We recommend moving to Google Cloud by running Parse’s server on App Engine. To do so, follow this guide.
Though we’re sad to see Parse shut down, it does not change our roadmap. Firebase and Google are committed to radically changing how app development is done, and we’re working hard towards that goal.
We have big plans for the future.
The third annual Static Showdown hackathon is coming February 20-21, 2016 -- and this year it's presented by Firebase!
The Static Showdown is a worldwide virtual competition where teams of up to four have 48 hours to build a web app with one catch: entries must be built without any custom server-side code. That means 100% HTML, JS, CSS, and off-the-shelf back-end services like the Firebase Database.
Registration has just opened, so get some friends together and build something awesome!
We're excited to continue the Static Showdown tradition started by Divshot. Past years have seen everything from music synthesizers to social networks, and we can't wait to see what everyone comes up with.
Today we’re adding support for Promises in the Firebase JavaScript SDK. Our promises are A+ compatible and their use is entirely optional.
Promises are an alternative to callbacks. They improve readability, simplify error handling, and decouple tasks into composable units. A Promise is a task that may not have finished yet. When a Promise's task finishes successfully the Promise is "fulfilled", otherwise it is "rejected." You interact with a Promise by calling its then method with callbacks that should be executed when the Promise is fulfilled or rejected.
then
Let's demonstrate the differences between callbacks and promises by building part of a blog webapp. Our first step is to fetch an article’s contents. Here is how it might look with callbacks:
ref.child('blogposts').child(id).once('value', function(snapshot) { // The callback succeeded; do something with the final result. renderBlog(snapshot.val()); }, function(error) { // The callback failed. console.error(error); });
The Promise-based implementation is similar:
ref.child('blogposts').child(id).once('value').then(function(snapshot) { // The Promise was "fulfilled" (it succeeded). renderBlog(snapshot.val()); }, function(error) { // The Promise was rejected. console.error(error); });
When your task has only one step, Promises and callbacks are almost identical. Promises shine when your task has multiple steps.
Promises are most useful when you compose them. The then method returns a new Promise and that Promise’s return value comes from the functions passed to then. Let’s create a simple utility function that fetches a blog post and returns the JS Object, not the DataSnapshot, at that location:
DataSnapshot
// Fetch a Blog Post by ID. Returns a Promise of an actual object, not a DataSnapshot. function getArticlePromise(id) { return ref.child('blogposts').child(id).once('value').then(function(snapshot) { return snapshot.val(); }); }
Now we can use getArticlePromise() and we get a Promise that does more than just fetch data from Firebase. This is especially useful when you want to transform Firebase data into a model in your application. You might also notice that we completely left error handling out of our sample--more about that later. Perhaps the greatest thing about then is the way it handles Promises returned by the functions you pass to then: if your function returned a Promise, the Promise returned by then will resolve or reject with the same value as the Promise you return. That’s a bit dense, so let’s illustrate the idea with a code sample. We are going to expand our blog app to fetch an article and update a read counter. The callback sample starts to get complicated:
getArticlePromise()
var articleRef = ref.child('blogposts').child(id); articleRef.once('value', function(article) { // The first callback succeeded; go to the second. articleRef.child('readCount').transaction(function(current) { // Increment readCount by 1, or set to 1 if it was undefined before. return (current || 0) + 1; }, function(error, committed, snapshot) { if (error) { // The fetch succeeded, but the update failed. console.error(error); } else { renderBlog({ article: article.val(), readCount: snapshot.val() }); } }); }, function(error) { // The fetch failed. console.error(error); });
The code handles errors in many places and we start to see the "Pyramid of Doom," the code indents deeper with every subtask. The Promise version is shorter, its indentation is simpler, and it doesn't worry about error handling until the end:
var article; var articleRef = ref.child('blogposts').child(id); articleRef.once('value').then(function(snapshot) { // The first promise succeeded. Save snapshot for later. article = snapshot.val(); // By returning a Promise, we know the function passed to "then" below // will execute after the transaction finishes. return articleRef.child('readCount').transaction(function(current) { // Increment readCount by 1, or set to 1 if it was undefined before. return (current || 0) + 1; }); }).then(function(readCountTxn) { // All promises succeeded. renderBlog({ article: article, readCount: readCountTxn.snapshot.val() }); }, function(error) { // Something went wrong. console.error(error); });
When you "chain" Promises with the then method, you can ignore errors until you are ready to handle them. Promises act like asynchronous try / catch blocks. You handle a rejected Promise by passing a second function to then. That second function is called instead of the first function if the Promise was rejected. If you don't pass a second function to then, the first function isn't called and the Promise that then returns is rejected with the same error that the previous Promise was rejected with.
try
catch
Firebase also supports a shorthand catch which only takes an error handler. catch is not part of the A+ standard, but is part of the new JavaScript built-in and most Promise libraries. Let’s demonstrate error handling by creating a getProfilePicPromise() utility:
getProfilePicPromise()
// Returns a Promise of a Blob function getProfilePicPromise(author) { return fetch(author.profileUrl).catch(function() { // By returning a new promise, we "recover" from errors in the first. return fetch(defaultProfileUrl); }); };
In this example, any failure to get the author's profile picture is handled by getting the default profile picture. If we successfully get the default profile picture, then getProfilePicPromise() succeeds. Calling catch or passing a second function to then recovers from the error, just like a catch block in synchronous code. Promises also have a version of "rethrowing" the error: you can literally throw an error or return a rejected Promise. To create a rejected Promise call Promise.reject(error).
Promise.reject(error)
The helper function Promise.all() takes an array of objects which can be Promises or regular values; the Promise returned by all() resolves to an array of the results of its inputs once they are all ready. We can use this to let our code do multiple things at once. Let’s expand our Promise-based sample once more by letting users "star" their favorite articles:
Promise.all()
all()
var getArticle = getArticlePromise(id); // After we get the article, automatically fetch the profile picture var getProfilePic = getArticle.then(function(article) { return getProfilePicPromise(article.author); }); // We can find out whether the article is starred without waiting on any other task. var getIsStarred = false; var authData = ref.getAuth(); if (authData) { var isStarredRef = ref.child('userStars').child(authData.uid).child(id); getIsStarred = isStarredRef.once('value').then(function(snapshot) { return snapshot.val() != null; }); } // Run all the requests then render the results. Promise.all([getArticle, getProfilePic, getIsStarred]).then(function(results) { renderBlog({ article: results[0], profilePic: results[1], isStarred: results[2], }); // We’ve fetched everything; increment the read count. return ref.child('blogposts').child(id).child('readCount').transaction(function(current) { return (current || 0) + 1; }); });
This code sample fetches an article and a profile picture for the article’s author (with support for fetching a default image) in sequence. While that sequence is happening, we fetch whether the current user has starred the article in parallel. When all information is fetched, we increment a read counter. The callback-implementation is sufficiently more complicated and is left as an exercise for the reader.
If you copy the samples in this post, be aware that they use some newer JavaScript built-ins: the fetch API and the Promise class. Firebase APIs return a Promise that works in all browsers. If you want to create your own Promises, consider using a library like Q. These Promise libraries let you write code that works on browsers that don't have the official Promise class yet.
All functions in Firebase that fire a one-time event now accept both Promise and callback-style methods. The following tables can help you translate your code to the Promise version:
auth(authToken, onComplete, onCancel)
/* Promise<AuthResult> */ auth(authToken)
authWithCustomToken(authToken, onComplete, [options])
/* Promise<AuthResult> */ authWithCustomToken(authToken, [options])
authAnonymously(onComplete, [options])
/* Promise<AuthResult> */ authAnonymously([options])
authWithPassword(credentials, onComplete, [options])
/* Promise<AuthResult> */ authWithPassword(credentials, [options])
authWithOAuthPopup(provider, credentials, onComplete, [options])
/* Promise<AuthResult> */ authWithOAuthPopup(provider, [options])
authWithOAuthRedirect(provider, onComplete, [options])
/* Promise<AuthResult> */ authWithOAuthRedirect(provider, [options])
authWithOAuthToken(provider, credentials, onComplete, [options])
/* Promise<AuthResult> */ authWithOAuthToken(provider, credentials, [options])
set(value, onComplete)
/* Promise<> */ set(value)
update(value, onComplete)
/* Promise<> */ update(value)
remove(value, onComplete)
/* Promise<> */ remove()
/* Firebase */ push(value, onComplete)
/* Firebase; which is also a Promise<Firebase> */ push(value)
setWithPriority(value, priority, onComplete)
/* Promise<> */ setWithPriority(value, priority)
setPriority(priority, onComplete)
/* Promise<> */ setPriority(priority)
transaction(updateFunction, onComplete, [applyLocally])
/* Promise<Object> */ transaction(updateFunction)
createUser(credentials, onComplete)
/* Promise<AuthResult> */ createUser(credentials)
changeEmail(credentials, onComplete)
/* Promise<> */ changeEmail(credentials)
changePassword(credentials, onComplete)
/* Promise<> */ changePassword(credentials)
removeUser(credentials, onComplete)
/* Promise<> */ removeUser(credentials)
resetPassword(credentials, onComplete)
/* Promise<> */ resetPassword(credentials)
Note: the result of the transaction method is a Promise for an object with two fields: committed and snapshot. These map to the two parameters passed to the onComplete callback.
transaction
committed
snapshot
onComplete
once(eventType, successCallback, failureCallback)
/* Promise<DataSnapshot> */ once(eventType)
cancel(onComplete)
/* Promise<> */ cancel()