Introducing FireSageJS, Ultimate Type Safety for Firebase Realtime Database
I made a library that solves Firebase Realtime Database typing problems.
It is the sister project of my other project, FirelordJS (for Firestore)
And similar to FirelordJS, FireSageJS comes with an extreme type safety mechanism without straying from the official library API.
You get the best of both worlds: low learning curve and safest types.
One image to convince you, runtime errors becomes compile time error:
If we use the equalTo cursor with other cursors such as endBefore, the Firebase SDK throws an error on runtime. With FireSageJS, we know this will happen the moment we type the code.
Quick start:
Define the Meta Type
import {
MetaTypeCreator,
ServerTimestamp,
PushAbleOnly,
NumericKeyRecord,
Removable,
} from 'firesagejs'
export type Example = MetaTypeCreator<{
a: 1 | 90 | 3700
b:
| {
c: boolean | Removable
d: {
e: ServerTimestamp
}
}
| Removable
f: Record<string, 'a' | 'b' | 'c'>
g: PushAbleOnly<{ h: number; j: { k: boolean } }>
i: NumericKeyRecord<string>
}>
Create Ref
import { Example } from './defineMetaType'
import { initializeApp } from 'firebase/app'
import { getDatabase, createRef } from 'firesagejs'
const app = initializeApp({
projectId: '### PROJECT ID ###',
})
export const db = getDatabase()
export const exampleRef = createRef<Example>(db)
Operation
import { exampleRef } from './createRef'
import {
set,
get,
update,
serverTimestamp,
remove,
push,
increment,
} from 'firesagejs'
;async () => {
await set(exampleRef('a'), 1)
await update(exampleRef(), ['b/c', 'b/d/e'], [true, serverTimestamp()])
const snapshot = await get(exampleRef('f'))
const val = snapshot.val()
const exists = snapshot.exists()
const size = snapshot.size
const hasChild = snapshot.hasChild('k')
const hasChildren = snapshot.hasChildren()
const json = snapshot.toJSON()
snapshot.forEach((child, index) => {
//
})
await remove(exampleRef('b/c'))
await push(exampleRef('g'), { h: increment(1), j: { k: true } })
}
Query
import { exampleRef } from './createRef'
import {
get,
orderByChild,
orderByKey,
orderByValue,
startAt,
startAfter,
endAt,
endBefore,
equalTo,
limitToFirst,
limitToLast,
query,
} from 'firesagejs'
;async () => {
const snapshot = await get(
query(
exampleRef('i'),
orderByValue(),
startAt('abc', '1'),
limitToFirst(4)
)
)
const snapshot2 = await get(
query(
exampleRef('f'),
orderByKey(),
endAt('abc'),
limitToLast(2)
)
)
const snapshot3 = await get(
query(
exampleRef('g'),
orderByChild('j/k'),
equalTo(false, 'a1b2c3'),
limitToLast(2)
)
)
}
Listener
import { exampleRef } from './createRef'
import {
orderByKey,
orderByValue,
startAt,
endAt,
limitToFirst,
limitToLast,
query,
onChildAdded,
onChildChanged,
onChildRemoved,
onChildMoved,
onValue,
} from 'firesagejs'
const unsub = onChildAdded(
query(exampleRef('i'), orderByValue(), startAt('abc', '1'), limitToFirst(4)),
snapshot => {}
)
const unsub2 = onChildChanged(
exampleRef('g'),
snapshot => {},
error => {}
)
const unsub3 = onChildRemoved(
query(exampleRef('f'), orderByKey(), endAt('abc'), limitToLast(2)),
snapshot => {},
{ onlyOnce: false }
)
const unsub4 = onChildMoved(
exampleRef('f'),
snapshot => {},
error => {},
{ onlyOnce: false }
)
const unsub6 = onValue(
exampleRef('b/d/e'),
snapshot => {},
error => {},
{ onlyOnce: false }
)
Transaction
import { exampleRef } from './createRef'
import { runTransaction } from 'firesagejs'
;async () => {
const result = await runTransaction(
exampleRef('g/a1b2c3'),
data => {
return { h: 123, j: { k: false } }
},
{ applyLocally: true }
)
const committed = result.committed
const snapshot = result.snapshot
const json = result.toJSON()
}
OnDisconnect
import { exampleRef } from './createRef'
import { serverTimestamp, onDisconnect } from 'firesagejs'
;async () => {
const onDc = onDisconnect(exampleRef('b'))
await onDc.set({ c: false, d: { e: serverTimestamp() } })
await onDc.update(['c', 'd'], [false, { e: serverTimestamp() }])
await onDc.remove()
await onDc.cancel()
}
There are a lot of things going on in this library, please read the documentation for more details
Long thing short, if you are looking for absolute RTDB type safety, this is it
Nothing else can offer the same level of type safety (because FireSageJS is the only RTDB type safe wrapper in existence)