Functional helpers for convenient work with Firestore.
Features
- build complex queries with ease by combining helpers with
pipe - improve the developer experience by defining default collection data type
- compatible with
firebase (web),firebase-admin (node)andreact-native-firebase - all functions are curried
- tiny size
Getting started
Installation
yarn add @mobily/firestore-fp
or with npm
npm install @mobily/firestore-fp --save
Prerequisites
Create a new function by using a query factory function to consume your Firestore queries.
firebase-admin
import { firestore } from 'firebase-admin' import { query } from '@mobily/firestore-fp' export const q = query(firestore)
firebase
import firebase from 'firebase' import 'firebase/firestore' import { query } from '@mobily/firestore-fp' export const q = query(firebase.firestore)
react-native-firebase@v6
import firestore from '@react-native-firebase/firestore' import { query } from '@mobily/firestore-fp' export const q = query(firestore)
If you use more than one Firebase app in your project you have to define a q function per app:
import { firestore } from 'firebase-admin' import { query } from '@mobily/firestore-fp' export const q = query(() => firestore({ /* app config */ }))
Examples
Reusable where helper.
import { collection, where, get } from '@mobily/firestore-fp' import { q } from './utils' interface Task { name: string isDone: boolean userId: string } interface Message { text: string userId: string } interface Room { name: string } const tasks = collection<Task>('tasks') const messages = collection<Message>('messages') const rooms = collection<Room>('rooms') const userIdEq = where('userId', '==') // return Promise<QuerySnapshot<Task>> const takeUserTasks = (userId: string) => q(tasks, userIdEq(userId), get) // return Promise<QuerySnapshot<Message>> const takeUserMessages = (userId: string) => q(messages, userIdEq(userId), get) // return Promise<QuerySnapshot<Room>> const takeUserRooms = (userId: string) => q(rooms, userIdEq(userId) /* error, property 'userId' doesn't exist on type 'Room' */, get)
Combine collection and doc helpers.
import { collection, doc, get, update, remove, add, set, pipe } from '@mobily/firestore-fp' import { q } from './utils' interface Task { name: string isDone: boolean userId: string } const tasks = collection<Task>('tasks') const tasksDoc = (id: string) => pipe(tasks, doc(id)) // return Promise<DocumentSnapshot<Task>> const takeTask = (id: string) => q(tasksDoc(id), get) // return Promise<void> const removeTask = (id: string) => q(tasksDoc(id), remove) // return Promise<DocumentReference<Task>> const addTask = (task: Task) => q(tasks, add(task)) // return Promise<void> const setTask = (id: string, task: Task) => q(tasksDoc(id), set({ ...task, createdAt: new Date() })) /* ⬆️ error: object literal may only specify known properties, and 'createdAt' does not exist in type 'Task' */ // return Promise<void> const toggleTaskDone = (id: string, isDone: boolean) => q(tasksDoc(id), update({ isDone } /* ◀️ Partial<Task> */))
Compound queries.
import { collection, where, orderBy, limit, get, pipe } from '@mobily/firestore-fp' import { q } from './utils' interface Task { name: string isDone: boolean userId: string createdAt: number } const tasks = collection<Task>('tasks') const whereIsDone = where('isDone', '==') const orderByCreatedAt = orderBy('createdAt') // return Promise<QuerySnapshot<Task>> const takeCompletedTasks = () => q(tasks, whereIsDone(true), orderByCreatedAt('asc'), get) const last10Completed = pipe( whereIsDone(true), orderByCreatedAt('desc'), limit(10) ) // return Promise<QuerySnapshot<Task>> const takeLast10CompletedTasks = () => q(tasks, last10Completed, get)
Query subcollections.
import { collection, doc, get } from '@mobily/firestore-fp' import { q } from './utils' interface Room { name: string } interface Message { userId: string text: string } const rooms = collection<Room>('rooms') const messages = collection<Message>('messages') // return Promise<QuerySnapshot<Message>> const takeRoomMessages = (roomId: string) => q(rooms, doc(roomId), messages, get)
Convert data by using withConverter.
import { O } from 'ts-toolbelt' import { collection, withConverter, get, DataConverter } from '@mobily/firestore-fp' import { q } from './utils' interface Task { name: string isDone: boolean userId: string createdAt: number } type ConvertedTask = O.Merge<{ createdAt: Date }, Task> const tasks = collection<Task>('tasks') const taskConverter: DataConverter<Task, ConvertedTask> = { toFirestore: data => data, fromFirestore: data => { return { ...data, createdAt: new Date(date.createdAt), } }, } // return Promise<QuerySnapshot<ConvertedTask>> const takeAllTasks = () => q(tasks, withConverter(taskConverter), get)
Api Reference
firestore-fp follows the official Firebase reference.
The current status is available here.
Contributors
License
The MIT License.
See LICENSE
