Skip to content

Instantly share code, notes, and snippets.

@JamieCurnow
Last active Aug 9, 2022
Embed
What would you like to do?
Using Firestore with Typescript
/**
* This Gist is part of a medium article - read here:
* https://jamiecurnow.medium.com/using-firestore-with-typescript-65bd2a602945
*/
// import firstore (obviously)
import { firestore } from "firebase-admin"
// Import or define your types
// import { YourType } from '~/@types'
interface YourType {
firstName: string
lastName: string
isGreat: boolean
blackLivesMatter: true
}
interface YourOtherType {
something: boolean
somethingElse: boolean
}
// This helper function pipes your types through a firestore converter
const converter = <T>() => ({
toFirestore: (data: Partial<T>) => data,
fromFirestore: (snap: FirebaseFirestore.QueryDocumentSnapshot) => snap.data() as T
})
// This helper function exposes a 'typed' version of firestore().collection(collectionPath)
// Pass it a collectionPath string as the path to the collection in firestore
// Pass it a type argument representing the 'type' (schema) of the docs in the collection
const dataPoint = <T>(collectionPath: string) => firestore().collection(collectionPath).withConverter(converter<T>())
// Construct a database helper object
const db = {
// list your collections here
users: dataPoint<YourType>('users'),
userPosts: (userId: string) => dataPoint<YourOtherType>(`users/${userId}/posts`)
}
// export your helper
export { db }
export default db
/**
* Some examples of how to use:
*/
const example = async (id: string) => {
// firestore just as you know it, but with types
const userDoc = await db.users.doc(id).get()
const { blackLivesMatter } = userDoc.data()
return blackLivesMatter === true // obviously
}
const createExample = async (userId: string) => {
await db.userPosts(userId).doc().create({
something: false,
somethingElse: true
})
}
// Always use set for updates as firestore doesn't type update function correctly yet!
const updateExample = async (id: string) => {
await db.users.doc(id).set({
firstName: 'Jamie',
blackLivesMatter: true
}, { merge: true })
}
@MatthewLymer
Copy link

MatthewLymer commented Sep 2, 2021

Why do you need to create a convert and call withConverter when you can simply cast the collection? All of the methods off of CollectionReference<T> are already typed (such as .doc, etc.)

const dataPoint = <T>(collectionPath: string) => firestore().collection(collectionPath) as CollectionReference<T>;

FireStore converters are intended to convert between the in-memory type representation and the persisted representation.

@spencercap
Copy link

spencercap commented Sep 24, 2021

🙌 NICE ! thanks @JamieCurnow

@yurist38
Copy link

yurist38 commented Oct 16, 2021

This helped a lot! Thanks @JamieCurnow

@michaelmok2021
Copy link

michaelmok2021 commented Dec 31, 2021

@JamieCurnow can you add to this GIST an example to return all objects in the users collection? Thanks.

@smikheiev
Copy link

smikheiev commented Jan 11, 2022

Thanks, @JamieCurnow! Nice stuff!
What about serverTimestamp? With serverTimestamp, toFirestore should have FieldValue type, and fromFirestore should have Timestamp type, but converter accepts only one data type. Is there a way to make it work? 🤔

@tohagan
Copy link

tohagan commented Jan 20, 2022

@smikheiev For your timestamp fields use this typescript type ...

export type Timestamp = admin.firestore.Timestamp | admin.firestore.FieldValue;

... so you can assign a value like ...

{ 
  ...
  updatedAt: FieldValue.serverTimestamp()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment