Thin layer around the firebase javascript API that allows you to query and declaratively update your favorite real-time database.
$ npm install cycle-firebase
import Cycle from '@cycle/core'
import {makeFirebaseDriver} from 'cycle-firebase'
import Firebase from 'firebase'
let main = ({firebase}) => {
// ... Code that uses firebase driver
}
Cycle.run(main, {
firebase: makeFirebaseDriver(new Firebase(YOUR_FIREBASE_URL)),
// ... Other drivers
})
I am not that good at describing API's, so PRs or issues are very welcome here :-)
The source consists of a couple of methods that will yields observables, like most cycle driver sources ;)
Returns an observable over the value on a specific location.
Will yield null, just like firebase, if there is nothing set.
This location is relative to the location of the firebase source
object (so .child affets this method).
There are two special locations you can listen for as well. Those are not coming from firebase, but provide info about the status of firebase in your app:
$lastError: Observable<Error>
Right now, is an observable over the last error emitted by the authentication
part of your app. I am still thinking about putting that in the same observable
as the user info itself (see next special location), but I figured it would be
fine putting it here as well. It contains error objects from firebase itself.
firebase.get(`$lastError/message`).map(message => `An error occurred: ${message}`)
$user: Observable<Null|User>
An observable over the current logged in user, or null. Use this instead of the
now deprecated firebase.uid$. User here, is the 'raw' object emitted by
firebases' onAuth method. It will emit once on subscription.
let isLoggedIn$ = firebase.get(`$user`).map(user => user !== null)
Returns object similar to the original source, only scoped
to the location specified, just like firebaseRef.child.
Useful if you want a component to be bound to a smaller scope of data.
Returns the firebase ref scoped to the same path as the firebase source.
Useful in combination with .value and .observe as you'll see...
Observable that will yield one random generated, firebase compatible, Id. This Id is generated exactly the way firebase would generate the Id for you, so you can use it safely as a key:
let someAction$ = ...
let listWithRandomKeys$ = someAction$.flatMapLatest(x => {
return firebase.pushId$.map(id => {
return {
[id]: x,
}
})
})
return {
firebase: listWithRandomKeys$.map(list => {
return { list }
})
}
Will transform a firebase query or ref into an observable over it's value. Useful for creating queries more complex than just a location.
let userQuery = firebase.child('users').ref().orderByChild('age').equalTo('19')
let user19yo$ = firebase.value(userQuery).map(...)
If you, for some weird reason, need to use any of the child_xxx events,
.observe is for you. It wraps the event eventName in a very simple way
so it becomes an observable. Most of the time, though, you want to use .value!
Just wraps an object in { $set: object } so the sink will
recognize it as an overwrite of data, not an update.
The sink expects the be fed object, describing the way you want the firebase DB to look.
It will assume any unmentioned key will be untouched, except for object from .$set.
It will look at the changes every time, and update the new keys.
For removal, you'll have to explicitly set a key to null. Just not including the
key anymore, won't make the driver remove it.
The $user location does not get mirrored in firebase, but instead updates
the current user logged in. This can be done by setting (with $set!!!) the location
to an object with a type property, and potentially more options.
The possible types are:
- { type: 'password', email, password } uses
authWithPassword - { type: 'password', create: true, email, password } creates a user using
createUserfollowed byauthWithPassword - { type: 'token', token } uses
authWithCustomToken - { type: 'anonymously' } uses
authAnonymously - { type: 'oauth_popup', provider } uses
authWithOAuthPopup - { type: 'oauth_token', provider, token } uses
authWithOAuthToken
If the object changed from
{
value: 1,
}
to
{
value: 2
}
it will initially set value to 1, (transition from {} to {value: 1} but
when the second object enters, it will, as one might expect, update the key
value to 2 on firebase.
If the object changed from
{
users: {
user1: {
name: 'Michiel Dral',
}
},
}
to
{
users: {
user2: {
name: 'Jake',
}
},
}
It will initially update the key users/user1/name to Michiel Dral, and then it
will update the key users/user2/name to Jake. Notice that it will
- Not update the
user1location after we drop it (doesn't remove it) - Not alter any other key than
users/user1/nameandusers/user2/name
If you want to overwrite (and not update, like happening normally) a value on
a key with an object, you can use firebase.$set like this:
{
users: {
user2: firebase.$set({
name: 'Jake',
})
},
}
this will wipe all the content of users/user2 and set it to just { name: 'Jake' }
I tried to comment the code as much as I could, and made the tests clear as well, there are indeed no tests for the source yet, I couldn't be bothered just yet. The styling is strict, and I want everything to obey it, so if something doesn't fit a styling rule, we'll remove the styling rule :P If you have a issue, please open one, or if you just want to discuss some stuff! I have never received pull requests for a project yet, so I am thrilled to do so(:
- Michiel Dral