Skip to main content

Advanced Usage

Once you’ve mastered basic CRUD, decorators, relations and hooks, explore these advanced Firestore features with FireODM.

1. Custom Converters

Sometimes you need to transform fields on the fly (e.g., encrypting, custom class instances). FireODM lets you register a FirestoreDataConverter:

import { FirestoreDataConverter, QueryDocumentSnapshot } from 'firebase-admin/firestore'
import { getFirestoreInstance } from 'fireodm/config'
import { BaseModel } from 'fireodm'

// Define converter
const MyConverter: FirestoreDataConverter<MyClass> = {
toFirestore(inst) {
return { raw: inst.toJSON() }
},
fromFirestore(snapshot: QueryDocumentSnapshot) {
const data = snapshot.data()
return new MyClass(data.raw)
}
}

// Apply converter manually if needed
const col = getFirestoreInstance()
.collection('mycol')
.withConverter(MyConverter)

2. Pagination & Cursors

Use findAll options to page through large datasets:

const page1 = await User.findAll({ limit: 10, orderBy: { field: 'createdAt', direction: 'asc' } })
const cursor = page1.lastVisible

const page2 = await User.findAll({
limit: 10,
orderBy: { field: 'createdAt', direction: 'asc' },
startAfter: cursor
})

Fields must be indexed in Firestore for composite queries.

3. Select & Projection

Fetch only specific fields to reduce bandwidth:

const { results } = await User.findAll({
queryFn: ref => ref.select('name','email'),
})
// Each User instance will only have 'name' and 'email' populated

4. Complex Queries

Combine where, in, array-contains, etc.:

// users whose role is one of ['admin','editor']
const users = await User.findWhere('role','in',['admin','editor'])

// posts tagged with 'news'
const newsPosts = await Post.findWhere('tags','array-contains','news')

5. GeoPoints & Timestamps

Decorate GeoPoint and Timestamp fields:

import { GeoPointField, TimestampField } from 'fireodm'
import { GeoPoint } from 'firebase-admin/firestore'

class Place extends BaseModel {
@GeoPointField()
location!: GeoPoint

@TimestampField({ defaultNow: true })
visitedAt?: Timestamp
}

// Query by bounding box
const nearby = await Place.findAll({
queryFn: ref => ref
.where('location', '>=', new GeoPoint(lat1, lng1))
.where('location', '<=', new GeoPoint(lat2, lng2))
})

6. Transactions & Batches (Recap)

Refer back to the Transactions & Batched Writes guide for atomic multi-op patterns.


Next: FAQ & Troubleshooting for common issues and best practices.