Convertidor que usa withConverter()
para transformar objetos de usuario de tipo AppModelType
en datos de Firestore de tipo DbModelType
.
El uso del conversor te permite especificar argumentos de tipo genérico cuando se almacenan y recuperan objetos de Firestore.
En este contexto, un “AppModel” es una clase que se usa en una aplicación para empaquetar información y funcionalidades relacionadas. Por ejemplo, esta clase podría tener propiedades con tipos de datos anidados complejos, propiedades usadas para la memorización, propiedades de tipos no compatibles con Firestore (como symbol
y bigint
) y funciones auxiliares que realizan operaciones compuestas. Estas clases no son adecuadas o no se pueden almacenar en una base de datos de Firestore. En cambio, las instancias de esas clases deben convertirse en "objetos antiguos y simples de JavaScript". (POJO) con propiedades exclusivamente primitivas, potencialmente anidadas dentro de otros POJO o arrays de POJO. En este contexto, el tipo se denomina "DbModel". y sería un objeto adecuado para permanecer en Firestore. Para mayor comodidad, las aplicaciones pueden implementar FirestoreDataConverter
y registrar el convertidor con objetos de Firestore, como DocumentReference
o Query
, para convertir automáticamente AppModel
en DbModel
cuando se almacenan en Firestore, y convertir DbModel
en AppModel
cuando se recuperan desde Firestore.
Firma:
export declare interface FirestoreDataConverter<AppModelType, DbModelType extends DocumentData = DocumentData>
Métodos
Método | Descripción |
---|---|
fromFirestore(instantánea, opciones) | El SDK de Firestore lo llama para convertir los datos de Firestore en un objeto de tipo AppModelType . Puedes acceder a tus datos llamando a snapshot.data(options) .Por lo general, los datos que muestra snapshot.data() se pueden convertir en DbModelType . Sin embargo, esto no está garantizado porque Firestore no aplica un esquema en la base de datos. Por ejemplo, las operaciones de escritura de una versión anterior de la aplicación o de otro cliente que no usó un convertidor de tipos podrían haber escrito datos con diferentes propiedades o tipos de propiedad. La implementación deberá elegir si se puede recuperar correctamente de los datos que no cumplen con los requisitos o si arroja un error.Para anular este método, consulta . |
toFirestore(modelObject) | El SDK de Firestore lo llama para convertir un objeto de modelo personalizado de tipo AppModelType en un objeto de JavaScript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType . Para usar set() con merge y mergeFields , toFirestore() se debe definir con PartialWithFieldValue<AppModelType> .El tipo WithFieldValue<T> extiende T para permitir que también se usen FieldValues, como deleteField(), como valores de propiedad. |
toFirestore(modelObject, opciones) | El SDK de Firestore lo llama para convertir un objeto de modelo personalizado de tipo AppModelType en un objeto de JavaScript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType . Se usa con setDoc() y con merge:true o mergeFields .El tipo PartialWithFieldValue<T> extiende Partial<T> para permitir que se usen FieldValues, como arrayUnion(), como valores de propiedad. También admite Partial anidados, ya que permite que se omitan los campos anidados. |
FirestoreDataConverter.fromFirestore()
El SDK de Firestore lo llama para convertir los datos de Firestore en un objeto de tipo AppModelType
. Para acceder a tus datos, llama al número snapshot.data(options)
.
Por lo general, los datos que muestra snapshot.data()
se pueden convertir en DbModelType
. Sin embargo, esto no está garantizado porque Firestore no aplica un esquema en la base de datos. Por ejemplo, las operaciones de escritura de una versión anterior de la aplicación o de otro cliente que no usó un convertidor de tipos podrían haber escrito datos con diferentes propiedades o tipos de propiedad. La implementación deberá elegir si se puede recuperar correctamente de los datos no conformes o si arroja un error.
Para anular este método, consulta .
Firma:
fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData, DocumentData>, options?: SnapshotOptions): AppModelType;
Parámetros
Parámetro | Tipo | Descripción |
---|---|---|
instantánea | QueryDocumentSnapshot<DocumentData y DocumentData> | Un QueryDocumentSnapshot que contenga tus datos y metadatos |
opciones | SnapshotOptions | El SnapshotOptions de la llamada inicial a data() . |
Muestra:
Tipo de modelo de aplicación
FirestoreDataConverter.toFirestore()
El SDK de Firestore lo llama para convertir un objeto de modelo personalizado de tipo AppModelType
en un objeto de JavaScript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType
. Para usar set()
con merge
y mergeFields
, toFirestore()
debe definirse con PartialWithFieldValue<AppModelType>
.
El tipo WithFieldValue<T>
extiende T
para permitir que también se usen FieldValues, como deleteField(), como valores de propiedad.
Firma:
toFirestore(modelObject: WithFieldValue<AppModelType>): WithFieldValue<DbModelType>;
Parámetros
Parámetro | Tipo | Descripción |
---|---|---|
Objeto de modelo | WithFieldValue<AppModelType> |
Muestra:
WithFieldValue<DbModelType>
FirestoreDataConverter.toFirestore()
El SDK de Firestore lo llama para convertir un objeto de modelo personalizado de tipo AppModelType
en un objeto de JavaScript simple (adecuado para escribir directamente en la base de datos de Firestore) de tipo DbModelType
. Se usa con setDoc() y con merge:true
o mergeFields
.
El tipo PartialWithFieldValue<T>
extiende Partial<T>
para permitir que FieldValues como arrayUnion() se usen como valores de propiedad. También admite Partial
anidados, ya que permite que se omitan los campos anidados.
Firma:
toFirestore(modelObject: PartialWithFieldValue<AppModelType>, options: SetOptions): PartialWithFieldValue<DbModelType>;
Parámetros
Parámetro | Tipo | Descripción |
---|---|---|
Objeto de modelo | PartialWithFieldValue<AppModelType> | |
opciones | SetOptions |
Muestra:
PartialWithFieldValue<DbModelType>
Ejemplo
Ejemplo simple
const numberConverter = {
toFirestore(value: WithFieldValue<number>) {
return { value };
},
fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions) {
return snapshot.data(options).value as number;
}
};
async function simpleDemo(db: Firestore): Promise<void> {
const documentRef = doc(db, 'values/value123').withConverter(numberConverter);
// converters are used with `setDoc`, `addDoc`, and `getDoc`
await setDoc(documentRef, 42);
const snapshot1 = await getDoc(documentRef);
assertEqual(snapshot1.data(), 42);
// converters are not used when writing data with `updateDoc`
await updateDoc(documentRef, { value: 999 });
const snapshot2 = await getDoc(documentRef);
assertEqual(snapshot2.data(), 999);
}
Ejemplo avanzado
// The Post class is a model that is used by our application.
// This class may have properties and methods that are specific
// to our application execution, which do not need to be persisted
// to Firestore.
class Post {
constructor(
readonly title: string,
readonly author: string,
readonly lastUpdatedMillis: number
) {}
toString(): string {
return `${this.title} by ${this.author}`;
}
}
// The PostDbModel represents how we want our posts to be stored
// in Firestore. This DbModel has different properties (`ttl`,
// `aut`, and `lut`) from the Post class we use in our application.
interface PostDbModel {
ttl: string;
aut: { firstName: string; lastName: string };
lut: Timestamp;
}
// The `PostConverter` implements `FirestoreDataConverter` and specifies
// how the Firestore SDK can convert `Post` objects to `PostDbModel`
// objects and vice versa.
class PostConverter implements FirestoreDataConverter<Post, PostDbModel> {
toFirestore(post: WithFieldValue<Post>): WithFieldValue<PostDbModel> {
return {
ttl: post.title,
aut: this._autFromAuthor(post.author),
lut: this._lutFromLastUpdatedMillis(post.lastUpdatedMillis)
};
}
fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): Post {
const data = snapshot.data(options) as PostDbModel;
const author = `${data.aut.firstName} ${data.aut.lastName}`;
return new Post(data.ttl, author, data.lut.toMillis());
}
_autFromAuthor(
author: string | FieldValue
): { firstName: string; lastName: string } | FieldValue {
if (typeof author !== 'string') {
// `author` is a FieldValue, so just return it.
return author;
}
const [firstName, lastName] = author.split(' ');
return {firstName, lastName};
}
_lutFromLastUpdatedMillis(
lastUpdatedMillis: number | FieldValue
): Timestamp | FieldValue {
if (typeof lastUpdatedMillis !== 'number') {
// `lastUpdatedMillis` must be a FieldValue, so just return it.
return lastUpdatedMillis;
}
return Timestamp.fromMillis(lastUpdatedMillis);
}
}
async function advancedDemo(db: Firestore): Promise<void> {
// Create a `DocumentReference` with a `FirestoreDataConverter`.
const documentRef = doc(db, 'posts/post123').withConverter(new PostConverter());
// The `data` argument specified to `setDoc()` is type checked by the
// TypeScript compiler to be compatible with `Post`. Since the `data`
// argument is typed as `WithFieldValue<Post>` rather than just `Post`,
// this allows properties of the `data` argument to also be special
// Firestore values that perform server-side mutations, such as
// `arrayRemove()`, `deleteField()`, and `serverTimestamp()`.
await setDoc(documentRef, {
title: 'My Life',
author: 'Foo Bar',
lastUpdatedMillis: serverTimestamp()
});
// The TypeScript compiler will fail to compile if the `data` argument to
// `setDoc()` is _not_ compatible with `WithFieldValue<Post>`. This
// type checking prevents the caller from specifying objects with incorrect
// properties or property values.
// @ts-expect-error "Argument of type { ttl: string; } is not assignable
// to parameter of type WithFieldValue<Post>"
await setDoc(documentRef, { ttl: 'The Title' });
// When retrieving a document with `getDoc()` the `DocumentSnapshot`
// object's `data()` method returns a `Post`, rather than a generic object,
// which would have been returned if the `DocumentReference` did _not_ have a
// `FirestoreDataConverter` attached to it.
const snapshot1: DocumentSnapshot<Post> = await getDoc(documentRef);
const post1: Post = snapshot1.data()!;
if (post1) {
assertEqual(post1.title, 'My Life');
assertEqual(post1.author, 'Foo Bar');
}
// The `data` argument specified to `updateDoc()` is type checked by the
// TypeScript compiler to be compatible with `PostDbModel`. Note that
// unlike `setDoc()`, whose `data` argument must be compatible with `Post`,
// the `data` argument to `updateDoc()` must be compatible with
// `PostDbModel`. Similar to `setDoc()`, since the `data` argument is typed
// as `WithFieldValue<PostDbModel>` rather than just `PostDbModel`, this
// allows properties of the `data` argument to also be those special
// Firestore values, like `arrayRemove()`, `deleteField()`, and
// `serverTimestamp()`.
await updateDoc(documentRef, {
'aut.firstName': 'NewFirstName',
lut: serverTimestamp()
});
// The TypeScript compiler will fail to compile if the `data` argument to
// `updateDoc()` is _not_ compatible with `WithFieldValue<PostDbModel>`.
// This type checking prevents the caller from specifying objects with
// incorrect properties or property values.
// @ts-expect-error "Argument of type { title: string; } is not assignable
// to parameter of type WithFieldValue<PostDbModel>"
await updateDoc(documentRef, { title: 'New Title' });
const snapshot2: DocumentSnapshot<Post> = await getDoc(documentRef);
const post2: Post = snapshot2.data()!;
if (post2) {
assertEqual(post2.title, 'My Life');
assertEqual(post2.author, 'NewFirstName Bar');
}
}