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!
ReactJS is a JavaScript library built by Facebook and Instagram which makes it easy to build large, complex user interfaces. It is intended to be used in combination with another framework which provides the backend. While Angular, Ember, and Backbone are popular choices for that, Firebase provides the easiest and quickest integration of a persistent, realtime backend into a React app - and it takes only a few lines of JavaScript.
Note: This post requires a basic knowledge of React. You can get up to speed quickly by looking through the short tutorial on the React homepage.
The creators of React describe it as the "V[iew] in MVC." It is not meant to be a replacement for Angular or Ember; instead, it is meant to extend their functionality by providing a high-performance way to keep a view up-to-date with JavaScript. Its special sauce is that it renders HTML using an incredibly fast virtual DOM diff algorithm, providing much better performance than competing platforms. It has a "one-way reactive data flow" which is much simpler to understand than traditional data-binding. Components - the basic building blocks of React apps - are organized in a tree hierarchy in which parent components send data down to their children through the props variable. Each component also has a state variable which determines the current data for its view. Whenever state is changed, the component's render() method is called and React figures out the most efficient way to update the DOM.
props
state
render()
Since React's main focus is on the user interface, apps made with it need something else to act as their backend. That is where Firebase comes in. It adds the "M[odel] and C[ontroller] in MVC" to React apps, making them fully functioning apps. Using React's straightforward binding system, it is easy to integrate Firebase in a native way with only a small amount of code.
Let's look at the Todo app on the React homepage. Within the TodoApp component, this.state is used to keep track of the input text and the list of Todo items. While React ensures that the DOM stays in sync with any changes to this.state, the changes do not persist beyond the life of the page. If you add a few items and then refresh the page, all of the items disappear! This is because React has no mechanism for storing data beyond the scope of the current page session. It relies on being used with another framework to do that.
TodoApp
this.state
Firebase is a natural complement to React as it provides React apps with a persistent, realtime backend. The first thing we need to do is add Firebase to the project:
<head> <!-- React JS --> <script src="http://fb.me/react-0.10.0.min.js"></script> <script src="http://fb.me/JSXTransformer-0.10.0.js"></script> <!-- Firebase JS --> <script src="https://cdn.firebase.com/js/client/1.0.17/firebase.js"></script> </head>
Now that we have included Firebase, we can populate the list of Todo items by reading them from Firebase. We do this by hooking into the componentWillMount() method of the TodoApp component which is run once, immediately before the initial rendering of the component:
componentWillMount()
componentWillMount: function() { this.firebaseRef = new Firebase("https://ReactFireTodoApp.firebaseio.com/items/"); this.firebaseRef.on("child_added", function(dataSnapshot) { this.items.push(dataSnapshot.val()); this.setState({ items: this.items }); }.bind(this)); }
This code first gets a reference to the items node at the root of the Firebase. The call to on() will be run every time a node is added under the items node. It is important to realize that a child_added event will be fired for every item under the items node in Firebase, not just new ones that are added to it. Therefore, when the page is loaded, every existing child under the items node will fire a child_added event, meaning they can easily be iterated over and added to this.state.items. Note that the call at the end to bind() just sets the scope of callback function to this.
items
on()
child_added
this.state.items
bind()
this
Now what about adding new Todo items to the Firebase? That code is just as easy:
handleSubmit: function(e) { e.preventDefault(); this.firebaseRef.push({ text: this.state.text }); this.setState({text: ""}); }
Within handleSubmit() a new item is pushed onto the saved Firebase reference which appends it to the end of the items node. The call to setState() updates this.state.text but does not need to update this.state.items as it did in the original React code. This is because the child_added event handler from componentWillMount() will be fired when a new child is pushed onto the items Firebase node and that code will update this.state.items.
handleSubmit()
setState()
this.state.text
The last thing that needs to happen is cleaning up the Firebase event handler:
componentWillUnmount: function() { this.firebaseRef.off(); }
With just the few changes above, items added to the Todo list are updated in realtime. Best of all, the items stick around if the page is refreshed! You can even open multiple tabs pointed at the same page and see them all update simultaneously, with Firebase doing all the heavy lifting. Take some time to view the code for this example and play around with a live demo (Note: it's the second one on the demo page).
Although integrating Firebase into a React app only takes a few lines of code out of the box, we wanted to make it even easier. That is why we built the ReactFireMixin for React which makes it simple to keep this.state in sync with a Firebase node.
We suggest you download ReactFire directly from our CDN:
<!-- ReactFire --> <script src="https://cdn.firebase.com/libs/reactfire/0.1.6/reactfire.min.js"></script>
ReactFire is also available from npm via npm install reactfire or from Bower via bower install reactfire.
npm install reactfire
bower install reactfire
To then use the ReactFireMixin in the TodoApp component, add it to the component's mixins property:
var TodoApp = React.createClass({ mixins: [ReactFireMixin], ... });
The ReactFireMixin extends the functionality of the TodoApp component, adding additional Firebase-specific methods to it. To keep this.state.items in sync with any changes to the items node in the Firebase, make the following change in componentWillMount():
componentWillMount: function() { this.bindAsArray(new Firebase("https://ReactFireTodoApp.firebaseio.com/items/"), "items"); }
We simply specify that we want to bind a particular Firebase reference to this.state.items of the React component. The ReactFireMixin allows binding to a Firebase node as an array or as a regular JavaScript object. This creates a one-way binding from the Firebase reference to this.state.items, meaning that if the data in the Firebase changes, so will this.state.items. However, if we update this.state.items, the Firebase will not change. Therefore, changes should be made directly to the Firebase and not by calling setState():
handleSubmit: function(e) { e.preventDefault(); this.firebaseRefs["items"].push({ text: this.state.text }); this.setState({text: ""}); }
The ReactFireMixin allows for binding to multiple things at once. Firebase ensures that this is all done in an efficient manner. To access the Firebase reference which is bound to this.state.items, we can reference this.firebaseRefs["items"] which is provided by the ReactFireMixin. Finally, calling this.firebaseRef.off() is no longer needed in componentWillUnmount() as the mixin handles this behind the scenes.
this.firebaseRefs["items"]
this.firebaseRef.off()
componentWillUnmount()
You can view the code for this example and play around with a live demo (Note: it's the last one on the demo page).
ReactJS is a wonderful framework for creating user interfaces. When picking a complementary tool to use alongside it as a backend, Firebase is the easiest and most powerful solution. In just a few lines of code you can get a React app syncing data across hundreds of clients at once. Our new ReactFireMixin makes this that much easier, getting rid of even more boilerplate code.
We encourage you to try out the new ReactFireMixin for React and let us know what you think. If you have any questions or feedback, post them in our Firebase Google Group or email me at jacob@firebase.com. Submit a pull request to our examples repo if you have any Firebase + React examples you would like to share.
We are excited to see what you create!