FirestoreDataConverter interface

withConverter()에서 AppModelType 유형의 사용자 객체를 DbModelType 유형의 Firestore 데이터로 변환하는 데 사용하는 변환기입니다.

변환기를 사용하면 Firestore에서 객체를 저장하고 검색할 때 일반 유형 인수를 지정할 수 있습니다.

이 컨텍스트에서 'AppModel'은 는 애플리케이션에서 관련 정보와 기능을 함께 패키징하는 데 사용되는 클래스입니다. 이러한 클래스에는 복잡하고 중첩된 데이터 유형의 속성, 메모에 사용되는 속성, Firestore에서 지원하지 않는 유형의 속성 (예: symbolbigint) 및 복합 작업을 수행하는 도우미 함수가 포함될 수 있습니다. 이러한 클래스는 Firestore 데이터베이스에 저장하기에 적합하지 않거나 저장할 수 없습니다. 대신 이러한 클래스의 인스턴스는 '기존 일반 JavaScript 객체'로 변환해야 합니다. (POJO) 원시 속성이 독점적으로 다른 POJO 또는 POJO 배열 내에 중첩될 가능성이 있습니다. 이 문맥에서는 이 유형을 'DbModel'이라고 합니다. Firestore에 유지하는 데 적합한 객체입니다. 편의를 위해 애플리케이션은 FirestoreDataConverter를 구현하고 DocumentReference 또는 Query와 같은 Firestore 객체에 변환기를 등록하여 Firestore에 저장할 때 AppModelDbModel로 자동 변환하고 Firestore에서 검색할 때 DbModelAppModel로 변환할 수 있습니다.

서명:

export declare interface FirestoreDataConverter<AppModelType, DbModelType extends DocumentData = DocumentData> 

메소드

메서드 설명
fromFirestore(스냅샷) Firestore 데이터를 AppModelType 유형의 객체로 변환하기 위해 Firestore SDK에서 호출합니다. snapshot.data()를 호출하여 데이터에 액세스할 수 있습니다.일반적으로 snapshot.data()에서 반환된 데이터는 DbModelType로 전송할 수 있습니다. Firestore는 데이터베이스에 스키마를 적용하지 않으므로 이것이 보장되지는 않습니다. 예를 들어 이전 버전의 애플리케이션에서 쓰기 또는 유형 변환기를 사용하지 않는 다른 클라이언트에서 쓰는 경우 다른 속성 또는 속성 유형으로 데이터를 쓸 수 있습니다. 구현 시 준수하지 않는 데이터에서 단계적으로 복구할지 또는 오류를 발생시킬지 선택해야 합니다.
toFirestore(modelObject) AppModelType 유형의 커스텀 모델 객체를 DbModelType 유형의 일반 JavaScript 객체 (Firestore 데이터베이스에 직접 작성하는 데 적합)로 변환하기 위해 Firestore SDK에서 호출합니다. setDoc() 및 .WithFieldValue<T> 유형은 T를 확장하여 deleteField()와 같은 FieldValue를 속성 값으로 사용할 수도 있습니다.
toFirestore(modelObject, options) AppModelType 유형의 커스텀 모델 객체를 DbModelType 유형의 일반 JavaScript 객체 (Firestore 데이터베이스에 직접 작성하는 데 적합)로 변환하기 위해 Firestore SDK에서 호출합니다. setDoc()merge:true 또는 mergeFields와 함께 사용됩니다.PartialWithFieldValue<T> 유형은 arrayUnion()과 같은 FieldValue를 속성 값으로 사용할 수 있도록 Partial<T>를 확장합니다. 또한 중첩된 필드를 생략할 수 있도록 하여 중첩된 Partial를 지원합니다.

FirestoreDataConverter.fromFirestore()

Firestore 데이터를 AppModelType 유형의 객체로 변환하기 위해 Firestore SDK에서 호출합니다. snapshot.data()를 호출하여 데이터에 액세스할 수 있습니다.

일반적으로 snapshot.data()에서 반환된 데이터를 DbModelType로 변환할 수 있습니다. Firestore는 데이터베이스에 스키마를 적용하지 않으므로 이것이 보장되지는 않습니다. 예를 들어 이전 버전의 애플리케이션에서 쓰기 또는 유형 변환기를 사용하지 않는 다른 클라이언트에서 쓰는 경우 다른 속성 또는 속성 유형으로 데이터를 쓸 수 있습니다. 구현 시 준수하지 않는 데이터에서 단계적으로 복구할지 또는 오류를 발생시킬지 선택해야 합니다.

서명:

fromFirestore(snapshot: QueryDocumentSnapshot<DocumentData, DocumentData>): AppModelType;

매개변수

매개변수 유형 설명
스냅샷 QueryDocumentSnapshot<DocumentData, DocumentData> 데이터와 메타데이터가 포함된 QueryDocumentSnapshot입니다.

반환:

앱 모델 유형

FirestoreDataConverter.toFirestore()

AppModelType 유형의 커스텀 모델 객체를 DbModelType 유형의 일반 JavaScript 객체 (Firestore 데이터베이스에 직접 작성하는 데 적합)로 변환하기 위해 Firestore SDK에서 호출합니다. setDoc() 및 .

WithFieldValue<T> 유형은 T를 확장하여 deleteField()와 같은 FieldValue를 속성 값으로 사용할 수도 있습니다.

서명:

toFirestore(modelObject: WithFieldValue<AppModelType>): WithFieldValue<DbModelType>;

매개변수

매개변수 유형 설명
modelObject WithFieldValue<AppModelType>

반환:

WithFieldValue<DbModelType>

FirestoreDataConverter.toFirestore()

AppModelType 유형의 커스텀 모델 객체를 DbModelType 유형의 일반 JavaScript 객체 (Firestore 데이터베이스에 직접 작성하는 데 적합)로 변환하기 위해 Firestore SDK에서 호출합니다. setDoc()merge:true 또는 mergeFields와 함께 사용됩니다.

PartialWithFieldValue<T> 유형은 Partial<T>를 확장하여 arrayUnion()과 같은 FieldValue를 속성 값으로 사용할 수 있도록 합니다. 또한 중첩된 필드를 생략할 수 있도록 하여 중첩된 Partial를 지원합니다.

서명:

toFirestore(modelObject: PartialWithFieldValue<AppModelType>, options: SetOptions): PartialWithFieldValue<DbModelType>;

매개변수

매개변수 유형 설명
modelObject PartialWithFieldValue<AppModelType>
옵션 SetOptions

반환:

PartialWithFieldValue<DbModelType>

간단한 예

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);
}

고급 예시

// 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');
    }
}