Greg and Mark are the Co-Founders of Pathwright, an online education platform built with Firebase and hosted on Firebase Hosting.
Pathwright is a platform for online education that allows anyone to teach and sell online courses under their own name and brand.
Education is something that is easy for us to get excited about. Most of our team is comprised of former or current professional educators. Drawing from past experiences, we saw a great opportunity to fix some of the problems we saw with online learning.
Most software developed for education has been designed to supplement classroom use, not to deliver a full education online. Additionally, the user experience for teachers and students is often very poor. We started Pathwright to give forward thinking teachers an easier, more attractive way to share knowledge online.
The most heavily trafficked system being powered by Firebase is our notification system. It allows us to notify students and teachers about important events that are happening in real time.
Secondly, we use Firebase to host our Pathwright.com marketing website. We previously hosted our website on Amazon S3 behind CloudFront, but the cost of using our pathwright.com SSL certificate with CloudFront was significant in comparison to Firebase.
Given that we are a small team, making use of Firebase has saved time and money from a development and operational standpoint. We have been able to continue working on our core platform instead of hassling with the additional infrastructure.
The automatic provisioning of SSL certificates was a time-saver, and a great value. Additionally the documentation was simple, and the whole process took about ten minutes from start to finish.
Since we were a very early adopter, we contacted support to ask about a feature that we didn’t see available yet. Firebase support responded quickly and helped us get a workaround in place within 24 hours, allowing us to re-launch with Firebase almost immediately. Customer service was fast, friendly, and thorough.
Firebase Hosting has been great for us and our customers. We recommend it without hesitation.
On the server side we’re running on Amazon Web Services. Some of the more critical pieces of our stack include Linux, Postgres, Python, Django.
On the client side we develop in CoffeeScript, Sass, and Backbone.js with Marionette.
We have a variety of customers covering a wide range of topics on Pathwright. Some of our favorites include Integral Calc, GetElected and Ligonier Connect.
GeoFire is an open-source JavaScript library that allows you to store and query a set of items based on their geographic location. GeoFire uses Firebase for data storage, allowing query results to be updated in realtime as they change. GeoFire does more than just measure the distance between locations; it selectively loads only the data near certain locations, keeping your applications light and responsive, even with extremely large datasets.
To showcase the power and simplicity of GeoFire, we built a demo which uses it alongside our Firebase Transit Open Data Set. The demo allows you to drag a circle around San Francisco and see all MUNI (the public transit system for the city) vehicles which are currently within it. Since GeoFire is built on top of Firebase, the demo updates in realtime as you drag around the circle and as new data comes in.
The GeoFire data for this demo is stored in a public Firebase which is freely available for use by any developer. We also provide GeoFire data for our latest open data set, launching today, Parking.
In addition to the MUNI demo, we created several other examples which show you how to perform common tasks in GeoFire. The code for all examples, including the MUNI demo, is available in the GeoFire GitHub repository.
A growing number of apps and websites - from car-sharing services to food recommendation apps to local event aggregators - alert users to places or things near them. While server-based solutions for geolocation queries are nothing new, there was nothing which allowed you to do them efficiently in Firebase. So we introduced GeoFire last September to provide you with an easy way to do client-side, on-the-fly filtering of geolocation data.
GeoFire 2.0 is a complete rewrite of that GeoFire library. We simplified the API and aligned it closer to the Firebase JavaScript client API you are already familiar with. You can now do even more with smaller, cleaner code. This version of GeoFire is now "production ready" - we have improved performance, added several common feature requests, and are ensuring its quality with a comprehensive test suite.
The best way to see what GeoFire can do is to jump right into the code. GeoFire has dependencies on Firebase (for realtime data storage) and RSVP (for an implementation of JavaScript promises), so we need to include them in our HTML file along with the GeoFire library:
<head> <!-- RSVP --> <script src="path/to/rsvp.min.js"></script> <!-- Firebase --> <script src="https://cdn.firebase.com/js/client/1.0.17/firebase.js"></script> <!-- GeoFire --> <script src="https://cdn.firebase.com/libs/geofire/2.0.0/geofire.min.js"></script> </head>
The necessary files are also available via Bower and npm. See the GeoFire documentation for full download instructions.
GeoFire stores its data at whatever Firebase location you choose to pass it:
var firebaseRef = new Firebase("https://<your-app>.firebaseio.com/geofire/"); var geoFire = new GeoFire(firebaseRef);
Once you have created a GeoFire instance, you can add locations to it using set(). It takes as input a unique, arbitrary key to represent the location as well as the location's latitude-longitude pair:
set()
geoFire.set("some_key", [37.785326, -122.405696]).then(function() { console.log("Provided key has been added to GeoFire"); }, function(error) { console.log("Error: " + error); });
To retrieve the location of a key at any time, pass the key to get():
get()
geoFire.get("some_key").then(function(location) { if (location === null) { console.log("Provided key is not in GeoFire"); } else { console.log("Provided key has a location of " + location); } }, function(error) { console.log("Error: " + error); });
GeoFire's real utility is shown by creating a GeoQuery. Say you are making an app for cyclists which shows the bicycle shops within a mile (1.609 kilometers) of a user's current location. The first thing you need to do is add the bike shops you care about to GeoFire. Then, create a query centered at the user's current location (let's say they are at [37.4, -122.6]):
GeoQuery
[37.4, -122.6]
var geoQuery = geoFire.query({ center: [37.4, -122.6], radius: 1.609 //kilometers });
A query on its own is not very useful. However, GeoFire allows you to add callback functions which are fired when important query events happen. Since you want to display every bike shop which matches the query criteria, listen for the key_entered event. Every time a key (that is, a bike shop) enters the query, the callback you defined will get called with data about that location:
key_entered
geoQuery.on("key_entered", function(key, location, distance) { console.log("Bicycle shop " + key + " found at " + location + " (" + distance + " km away)"); });
It is important to realize that the key_entered event works like typical Firebase events. That is, it will be fired for every key in GeoFire which matches the query criteria, both keys which are already in GeoFire and those which are added at any point in the future. Thanks to Firebase, you receive these events in realtime.
GeoFire is smart about how it looks for keys within the query. It does not need to load all of the GeoFire data into memory. If your user is looking for bicycle shops in San Francisco, GeoFire will not load data for locations in New York only to realize that they are on the opposite side of the country. It only checks on locations which are actually nearby. This keeps your app light and responsive, regardless of how big your data set is.
If your user is biking around while looking for a shop to visit, it is possible that shops which were within a mile of them are now further away. To deal with this, GeoFire will fire the key_exited event for each key which leaves the query:
key_exited
geoQuery.on("key_exited", function(key, location, distance) { console.log("Bicycle shop " + key + " left query to " + location + " (" + distance + " km away)"); });
That is really all you need to make your app work. GeoFire offers other features not covered here, including:
To learn more about these features, read the full documentation and view the code samples.
GeoFire 2.0 is available today and is ready to be integrated into your website or app. If you have any questions or feedback, post them in our Firebase Google Group or reach out to us on Twitter. We cannot wait to see what you build with GeoFire!
Cy is the Founder of Crowdsound, a realtime audience engagement tool built with Firebase and Angular.
Crowdsound is a one-stop shop for live events.
Whether you’re creating a talk, a panel, a seminar, a news show, or any live event, you can interact with your audience in real time. Crowdsound allows you to look at your audience’s demographics, take questions from audience members, quickly poll them, and follow up with them later.
On top of that, Crowdsound gives those audience members a say in the conversation. They become more than just users: they become participants. They can even message each other to stay in touch after the event is over. No more fumbling to to exchange contact info.
It began with trying to get feedback on a talk of ours. While we were trying to solve this problem, we realized that there was a bigger issue at hand. How could we get active participation from an audience? The problem became clearer when the event got broadcasted live to a virtual audience.
Right now, most events are like one-way streets: you simply deliver content to passive viewers and listeners. We were inspired by the idea of finally giving audience members active voices in the conversation, regardless of where they’re located.
The first time I witnessed a speaker answer my question during a livestream was exhilarating. I felt included in the event and immediately invited more of my friends. We want more people to feel exhilarated--to feel like they’re being heard. We want to enable large groups of people to share their ideas and collaborate to reach their goals. That’s what motivates us at Crowdsound.
Because of the real-time nature of our app, we built it completely on Firebase and Angular. We are truly putting Firebase’s capabilities to the test.
It was clear we needed a tool to send live data changes to thousands of devices simultaneously. Without Firebase, we would’ve needed a much bigger team, and that team would’ve taken months to build what we’ve built in just a couple of weeks.
In addition to AngularFire we’re using a custom implementation of Kato Richardson’s synchronized array.
We have a Node server on Heroku and static assets on Amazon S3. The great thing about Firebase (and its Security and Firebase Rules) is that it gives us a centralized place to exchange data. That way, we can use Firebase as an API to our own servers and make more complex queries. It’s especially useful when we need to crunch analytics information for our customers.
Professors using Crowdsound to quiz their students in real time. It's great to see them quickly getting a pulse on how well their students are absorbing the material.
In a classroom setting, we’re deterred from speaking up when we don't understand a concept because we don’t want our peers to think we’re not as smart as they are. It’s human nature. Unfortunately, what often happens is that half the class doesn’t understand something, but no one actually wants to speak up. Crowdsound lets teachers test their students anonymously on the fly and use the results to focus on the concepts that actually need extra attention.
We’re excited to share that native video streaming and private rooms are coming very soon. After witnessing our customers fidgeting with Google Hangouts that only allow 10 users and other streaming services that take forever to set up, we wanted to create a one-stop solution with everything you need to run your live event out of the box.
Today we’re excited to launch Firebase Hosting, production-grade hosting for developers. Firebase Hosting is designed for use by mission-critical sites and apps. We’ve built it from the ground up for security, reliability, and scale.
No other hosting service matches these features.
On top of this, we’ve made Hosting incredibly simple to use by providing one-command deploy and one-click rollback. Developers now have a fast, secure, and reliable way to host their apps.
Once upon a time, in the dark ages of the internet, apps dynamically generated HTML server-side every time a page was requested. This was slow, difficult to scale, and required users to connect to distant servers to get their content.
Even worse, this required clients to talk to a server on every state transition. For mobile users, this meant applications would break the moment connectivity was lost.
With the advent of fast JavaScript engines and powerful mobile devices, client-side page generation has become common. Angular, Ember, React, and a number of other client-side rendering technologies have transformed the application stack. Servers no longer need to send different HTML to each client. Instead, a single optimized set of static assets can be served to all clients. These static assets can then be placed on a geographically-distributed CDN, meaning users need only load data from a nearby point-of-presence (POP) rather than a server on the other side of the planet.
This new model has advantages for both end users and developers:
Faster Load Times: Static assets are cacheable and can be served from a local CDN POP.
Smoother State Transitions: Apps no longer need to load HTML from servers to move from page to page.
Apps Work Offline: Application logic runs on the device, meaning apps remain responsive even without network connectivity.
Easier to Scale: Delivering static content is easier than scaling server processes.
Less Complex Operations: Fewer servers to worry about means you can spend more time writing code.
Better Cross-Platform Support: Since all dynamic data is loaded from an API, it’s easier to build for mobile, web, IoT, etc.
Static apps load dynamic, personalized data from an API. Firebase is a popular choice for this API since we eliminate the need for server-side code and allow apps to deliver data changes in realtime. We’ve been thrilled to be a part of this shift and are especially excited about the popularity of our bindings for Angular, Ember, Backbone, and React.
However, providing only an API for dynamic data left our developers needing to find their own solution for hosting their assets: HTML, JavaScript, images, etc.
You, our developer community, voiced your frustration with existing hosting solutions. None of them had the same level of ease, reliability, and polish as the Firebase API. Since hosting is such an integral part of app development, we chose to solve the problem ourselves.
It means we've thought hard about how hosting should work. Firebase Hosting includes:
1. One-command deploys; one-click rollbacks
Deploy your content in seconds with the Firebase command line interface. Once uploaded, content begins serving immediately. With one click in the App Dashboard you can rollback to an earlier version and it will begin serving immediately. There’s no need to re-upload an old version if you’ve made a mistake, saving you precious time.
2. SSL by default
Every app on Firebase Hosting is served over HTTPS. Gone is the pain and expense of buying and installing an SSL certificate. From acquiring the cert to securing your domain, we do it all for you.
3. Your own domain
If you have a paid account, you can serve content from any custom domain, even a naked domain. Using our free plan? Don’t worry, you’ll get all the other advantages of Firebase Hosting on your own firebaseapp.com subdomain, for free!
4. Lightning fast content delivery
With Firebase Hosting you get a global footprint out of the box. We’re using one of the fastest CDNs available. Content is cached at POPs in North America, Europe, and Asia Pacific on SSDs, meaning no matter where your users are, they get your content fast.
5. Straight-forward pricing
We’re rolling Firebase Hosting into our existing plans, without raising the price! If you’re on our free Hacker plan you get:
Firebase Hosting is available starting today. Over 1,000 apps and sites have been deployed in our beta period and we're using it to host Firebase.com!
Get started by signing up. For more details, check out the documentation.
Welcome to the future of hosting!
Chris RaynorFirebase Hosting Lead
In our last blog post, we discussed best practices for using arrays in Firebase. If you haven't read that, please start there. This blog post will discuss advanced concepts for converting between real-time objects and sortable, filterable arrays.
Okay, so let's get some working gloves on and start handling synchronized arrays. If we want client-side sorting, filtering, and other things arrays are great at, but also need the awesome collaborative aspects of real-time objects, then we're going to be up to our elbows in code. We're going to work in JavaScript here, because that's what I'm most familiar with. All of these techniques can be translated to iOS and Android as well.
Before we get started, it's worth mentioning that there are some great frameworks that have already resolved the problems we face with synchronized arrays. In fact, we'll be drawing from the principles of their array management throughout this article.
EmberFire provides array handling out of the box, integrating Firebase neatly into the Ember.js framework. If you're already a glowing Ember dev, check out the Ember + Firebase Guide for details.
AngularFire version 0.8.0 provides utilities for using arrays in Angular. If you are an ngDev, Check out the Angular + Firebase Guide for details.
BackboneFire provides similar capabilities for those osteologists who prefer Backbone's flexible MVVM.
Drawing from the lessons learned in developing libraries like AngularFire, we can lay down a solid set of principles to keep ourselves out of trouble:
In other words, our array is essentially a one-directional loop. Changes come from the server into the array, we read them out, we push our local edits to the server, they trickle back into the array.
Okay, let's get started handling synchronized arrays in Firebase.
Let's start simple:
function getSynchronizedArray(firebaseRef) { var list = []; // put some magic here return list; }
This provides a method we can pass a firebaseRef into and get back an array. Now let's put some data into it.
function getSynchronizedArray(firebaseRef) { var list = []; syncChanges(list, firebaseRef); return list; } function syncChanges(list, ref) { ref.on('child_added', function _add(snap, prevChild) { var data = snap.val(); data.$id = snap.key(); // assumes data is always an object var pos = positionAfter(list, prevChild); list.splice(pos, 0, data); }); } // similar to indexOf, but uses id to find element function positionFor(list, key) { for(var i = 0, len = list.length; i < len; i++) { if( list[i].$id === key ) { return i; } } return -1; } // using the Firebase API's prevChild behavior, we // place each element in the list after it's prev // sibling or, if prevChild is null, at the beginning function positionAfter(list, prevChild) { if( prevChild === null ) { return 0; } else { var i = positionFor(list, prevChild); if( i === -1 ) { return list.length; } else { return i+1; } } }
Okay, now our array is getting populated from Firebase. Next, let's deal with the other CRUD operations, and move events.
function syncChanges(list, ref) { ref.on('child_added', ...); // example above ref.on('child_removed', function _remove(snap) { var i = positionFor(list, snap.key()); if( i > -1 ) { list.splice(i, 1); } }); ref.on('child_changed', function _change(snap) { var i = positionFor(list, snap.key()); if( i > -1 ) { list[i] = snap.val(); list[i].$id = snap.key(); // assumes data is always an object } }); ref.on('child_moved', function _move(snap, prevChild) { var curPos = positionFor(list, snap.key()); if( curPos > -1 ) { var data = list.splice(curPos, 1)[0]; var newPos = positionAfter(list, prevChild); list.splice(newPos, 0, data); } }); }
Great, now our array is completely synchronized with the remote data. Now we just need some local editing capabilities.
We won't modify our array directly. We can do ops like sort to re-order the data, but if we start trying todo ops like splice or pop, and have the server modifying this array as well, things will get ugly.
sort
splice
pop
Instead, we'll directly push changes to the server with some quick wrapper methods. Since Firebase triggers eventsfor local changes immediately, without waiting for a server response, this is quite performant and simplifies theprocess as well:
function getSynchronizedArray(firebaseRef) { var list = []; syncChanges(list, firebaseRef); wrapLocalCrudOps(list, firebaseRef); return list; } function syncChanges() { /* in examples above */ } function wrapLocalCrudOps(list, firebaseRef) { // we can hack directly on the array to provide some convenience methods list.$add = function(data) { return firebaseRef.push(data); }; list.$remove = function(key) { firebaseRef.child(key).remove(); }; list.$set = function(key, newData) { // make sure we don't accidentally push our $id prop if( newData.hasOwnProperty('$id') ) { delete newData.$id; } firebaseRef.child(key).set(newData); }; list.$indexOf = function(key) { return positionFor(list, key); // positionFor in examples above } }
And there we are! We can now manipulate our array by using operations like this:
var ref = new Firebase(URL); var list = getSynchronizedArray(ref); // add a record var newRecordId = list.$add({ foo: 'bar' }).key(); // remove a record list.$remove(recordKey); // update a record var data = list[5]; data.foo = 'baz'; list.$set( data.$id, data );
That's pretty much it! There are a few additional edge cases that we can cover, but we've created asynchronized array that merges local changes with server updates and keeps all of it in a tidy array.
Here's an example project utilizing these principles you can useas a template for baking your own solutions. Check out the README for installation instructions.
At Firebase, we're never satisfied with just being awesome. We are constantly working to improve all these scenarios and have several API enhancements in the pipeline. Keep an eye on the Google Group for details and announcements.
Find this article useful or have additional questions? Leave a comment below or post a message on the Google Group!