Skip to content

Feat(Firestore) JSON serialization of types to improve SSR support. #8926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 23, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions common/api-review/firestore-lite.api.md
Original file line number Diff line number Diff line change
@@ -68,9 +68,11 @@ export function average(field: string | FieldPath): AggregateField<number | null
// @public
export class Bytes {
static fromBase64String(base64: string): Bytes;
static fromJSON(json: object): Bytes;
static fromUint8Array(array: Uint8Array): Bytes;
isEqual(other: Bytes): boolean;
toBase64(): string;
toJSON(): object;
toString(): string;
toUint8Array(): Uint8Array;
}
@@ -135,9 +137,12 @@ export function documentId(): FieldPath;
export class DocumentReference<AppModelType = DocumentData, DbModelType extends DocumentData = DocumentData> {
readonly converter: FirestoreDataConverter<AppModelType, DbModelType> | null;
readonly firestore: Firestore;
static fromJSON(firestore: Firestore, json: object): DocumentReference;
static fromJSON<NewAppModelType = DocumentData, NewDbModelType extends DocumentData = DocumentData>(firestore: Firestore, json: object, converter: FirestoreDataConverter<NewAppModelType, NewDbModelType>): DocumentReference<NewAppModelType, NewDbModelType>;
get id(): string;
get parent(): CollectionReference<AppModelType, DbModelType>;
get path(): string;
toJSON(): object;
readonly type = "document";
withConverter<NewAppModelType, NewDbModelType extends DocumentData = DocumentData>(converter: FirestoreDataConverter<NewAppModelType, NewDbModelType>): DocumentReference<NewAppModelType, NewDbModelType>;
withConverter(converter: null): DocumentReference<DocumentData, DocumentData>;
@@ -205,12 +210,14 @@ export type FirestoreErrorCode = 'cancelled' | 'unknown' | 'invalid-argument' |
// @public
export class GeoPoint {
constructor(latitude: number, longitude: number);
static fromJSON(json: object): GeoPoint;
isEqual(other: GeoPoint): boolean;
get latitude(): number;
get longitude(): number;
toJSON(): {
latitude: number;
longitude: number;
type: string;
};
}

@@ -416,6 +423,7 @@ export class Timestamp {
seconds: number,
nanoseconds: number);
static fromDate(date: Date): Timestamp;
static fromJSON(json: object): Timestamp;
static fromMillis(milliseconds: number): Timestamp;
isEqual(other: Timestamp): boolean;
readonly nanoseconds: number;
@@ -425,6 +433,7 @@ export class Timestamp {
toJSON(): {
seconds: number;
nanoseconds: number;
type: string;
};
toMillis(): number;
toString(): string;
@@ -466,8 +475,10 @@ export function vector(values?: number[]): VectorValue;
// @public
export class VectorValue {
/* Excluded from this release type: __constructor */
static fromJSON(json: object): VectorValue;
isEqual(other: VectorValue): boolean;
toArray(): number[];
toJSON(): object;
}

// @public
65 changes: 65 additions & 0 deletions common/api-review/firestore.api.md
Original file line number Diff line number Diff line change
@@ -68,9 +68,11 @@ export function average(field: string | FieldPath): AggregateField<number | null
// @public
export class Bytes {
static fromBase64String(base64: string): Bytes;
static fromJSON(json: object): Bytes;
static fromUint8Array(array: Uint8Array): Bytes;
isEqual(other: Bytes): boolean;
toBase64(): string;
toJSON(): object;
toString(): string;
toUint8Array(): Uint8Array;
}
@@ -161,9 +163,12 @@ export function documentId(): FieldPath;
export class DocumentReference<AppModelType = DocumentData, DbModelType extends DocumentData = DocumentData> {
readonly converter: FirestoreDataConverter<AppModelType, DbModelType> | null;
readonly firestore: Firestore;
static fromJSON(firestore: Firestore, json: object): DocumentReference;
static fromJSON<NewAppModelType = DocumentData, NewDbModelType extends DocumentData = DocumentData>(firestore: Firestore, json: object, converter: FirestoreDataConverter<NewAppModelType, NewDbModelType>): DocumentReference<NewAppModelType, NewDbModelType>;
get id(): string;
get parent(): CollectionReference<AppModelType, DbModelType>;
get path(): string;
toJSON(): object;
readonly type = "document";
withConverter<NewAppModelType, NewDbModelType extends DocumentData = DocumentData>(converter: FirestoreDataConverter<NewAppModelType, NewDbModelType>): DocumentReference<NewAppModelType, NewDbModelType>;
withConverter(converter: null): DocumentReference<DocumentData, DocumentData>;
@@ -178,8 +183,15 @@ export class DocumentSnapshot<AppModelType = DocumentData, DbModelType extends D
get id(): string;
readonly metadata: SnapshotMetadata;
get ref(): DocumentReference<AppModelType, DbModelType>;
toJSON(): object;
}

// @public
export function documentSnapshotFromJSON(db: Firestore, json: object): DocumentSnapshot;

// @public
export function documentSnapshotFromJSON<AppModelType, DbModelType extends DocumentData = DocumentData>(db: Firestore, json: object, converter: FirestoreDataConverter<AppModelType, DbModelType>): DocumentSnapshot<AppModelType, DbModelType>;

export { EmulatorMockTokenOptions }

// @public @deprecated
@@ -264,12 +276,14 @@ export interface FirestoreSettings {
// @public
export class GeoPoint {
constructor(latitude: number, longitude: number);
static fromJSON(json: object): GeoPoint;
isEqual(other: GeoPoint): boolean;
get latitude(): number;
get longitude(): number;
toJSON(): {
latitude: number;
longitude: number;
type: string;
};
}

@@ -459,6 +473,46 @@ export function onSnapshot<AppModelType, DbModelType extends DocumentData>(query
// @public
export function onSnapshot<AppModelType, DbModelType extends DocumentData>(query: Query<AppModelType, DbModelType>, options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot<AppModelType, DbModelType>) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, onNext: (snapshot: QuerySnapshot<AppModelType, DbModelType>) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, onNext: (snapshot: DocumentSnapshot<AppModelType, DbModelType>) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, onNext: (snapshot: QuerySnapshot<AppModelType, DbModelType>) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, onNext: (snapshot: DocumentSnapshot<AppModelType, DbModelType>) => void, onError?: (error: FirestoreError) => void, onCompletion?: () => void, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, observer: {
next: (snapshot: QuerySnapshot<AppModelType, DbModelType>) => void;
error?: (error: FirestoreError) => void;
complete?: () => void;
}, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, observer: {
next: (snapshot: DocumentSnapshot<AppModelType, DbModelType>) => void;
error?: (error: FirestoreError) => void;
complete?: () => void;
}, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, observer: {
next: (snapshot: QuerySnapshot<AppModelType, DbModelType>) => void;
error?: (error: FirestoreError) => void;
complete?: () => void;
}, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotResume<AppModelType, DbModelType extends DocumentData>(firestore: Firestore, snapshotJson: object, options: SnapshotListenOptions, observer: {
next: (snapshot: DocumentSnapshot<AppModelType, DbModelType>) => void;
error?: (error: FirestoreError) => void;
complete?: () => void;
}, converter?: FirestoreDataConverter<DbModelType>): Unsubscribe;

// @public
export function onSnapshotsInSync(firestore: Firestore, observer: {
next?: (value: void) => void;
@@ -610,8 +664,15 @@ export class QuerySnapshot<AppModelType = DocumentData, DbModelType extends Docu
readonly metadata: SnapshotMetadata;
readonly query: Query<AppModelType, DbModelType>;
get size(): number;
toJSON(): object;
}

// @public
export function querySnapshotFromJSON(db: Firestore, json: object): QuerySnapshot;

// @public
export function querySnapshotFromJSON<AppModelType, DbModelType extends DocumentData = DocumentData>(db: Firestore, json: object, converter: FirestoreDataConverter<AppModelType, DbModelType>): QuerySnapshot<AppModelType, DbModelType>;

// @public
export class QueryStartAtConstraint extends QueryConstraint {
readonly type: 'startAt' | 'startAfter';
@@ -696,6 +757,7 @@ export class Timestamp {
seconds: number,
nanoseconds: number);
static fromDate(date: Date): Timestamp;
static fromJSON(json: object): Timestamp;
static fromMillis(milliseconds: number): Timestamp;
isEqual(other: Timestamp): boolean;
readonly nanoseconds: number;
@@ -705,6 +767,7 @@ export class Timestamp {
toJSON(): {
seconds: number;
nanoseconds: number;
type: string;
};
toMillis(): number;
toString(): string;
@@ -751,8 +814,10 @@ export function vector(values?: number[]): VectorValue;
// @public
export class VectorValue {
/* Excluded from this release type: __constructor */
static fromJSON(json: object): VectorValue;
isEqual(other: VectorValue): boolean;
toArray(): number[];
toJSON(): object;
}

// @public
39 changes: 39 additions & 0 deletions docs-devsite/firestore_.bytes.md
Original file line number Diff line number Diff line change
@@ -23,9 +23,11 @@ export declare class Bytes
| Method | Modifiers | Description |
| --- | --- | --- |
| [fromBase64String(base64)](./firestore_.bytes.md#bytesfrombase64string) | <code>static</code> | Creates a new <code>Bytes</code> object from the given Base64 string, converting it to bytes. |
| [fromJSON(json)](./firestore_.bytes.md#bytesfromjson) | <code>static</code> | Builds a <code>Bytes</code> instance from a JSON object created by [Bytes.toJSON()](./firestore_.bytes.md#bytestojson)<!-- -->. |
| [fromUint8Array(array)](./firestore_.bytes.md#bytesfromuint8array) | <code>static</code> | Creates a new <code>Bytes</code> object from the given Uint8Array. |
| [isEqual(other)](./firestore_.bytes.md#bytesisequal) | | Returns true if this <code>Bytes</code> object is equal to the provided one. |
| [toBase64()](./firestore_.bytes.md#bytestobase64) | | Returns the underlying bytes as a Base64-encoded string. |
| [toJSON()](./firestore_.bytes.md#bytestojson) | | Returns a JSON-serializable representation of this <code>Bytes</code> instance. |
| [toString()](./firestore_.bytes.md#bytestostring) | | Returns a string representation of the <code>Bytes</code> object. |
| [toUint8Array()](./firestore_.bytes.md#bytestouint8array) | | Returns the underlying bytes in a new <code>Uint8Array</code>. |

@@ -49,6 +51,28 @@ static fromBase64String(base64: string): Bytes;

[Bytes](./firestore_.bytes.md#bytes_class)

## Bytes.fromJSON()

Builds a `Bytes` instance from a JSON object created by [Bytes.toJSON()](./firestore_.bytes.md#bytestojson)<!-- -->.

<b>Signature:</b>

```typescript
static fromJSON(json: object): Bytes;
```

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| json | object | a JSON object represention of a <code>Bytes</code> instance |

<b>Returns:</b>

[Bytes](./firestore_.bytes.md#bytes_class)

an instance of [Bytes](./firestore_.bytes.md#bytes_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs.

## Bytes.fromUint8Array()

Creates a new `Bytes` object from the given Uint8Array.
@@ -106,6 +130,21 @@ string

The Base64-encoded string created from the `Bytes` object.

## Bytes.toJSON()

Returns a JSON-serializable representation of this `Bytes` instance.

<b>Signature:</b>

```typescript
toJSON(): object;
```
<b>Returns:</b>

object

a JSON representation of this object.

## Bytes.toString()

Returns a string representation of the `Bytes` object.
65 changes: 65 additions & 0 deletions docs-devsite/firestore_.documentreference.md
Original file line number Diff line number Diff line change
@@ -33,6 +33,9 @@ export declare class DocumentReference<AppModelType = DocumentData, DbModelType

| Method | Modifiers | Description |
| --- | --- | --- |
| [fromJSON(firestore, json)](./firestore_.documentreference.md#documentreferencefromjson) | <code>static</code> | Builds a <code>DocumentReference</code> instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson)<!-- -->. |
| [fromJSON(firestore, json, converter)](./firestore_.documentreference.md#documentreferencefromjson) | <code>static</code> | Builds a <code>DocumentReference</code> instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson)<!-- -->. |
| [toJSON()](./firestore_.documentreference.md#documentreferencetojson) | | Returns a JSON-serializable representation of this <code>DocumentReference</code> instance. |
| [withConverter(converter)](./firestore_.documentreference.md#documentreferencewithconverter) | | Applies a custom data converter to this <code>DocumentReference</code>, allowing you to use your own custom model objects with Firestore. When you call [setDoc()](./firestore_lite.md#setdoc_ee215ad)<!-- -->, [getDoc()](./firestore_lite.md#getdoc_4569087)<!-- -->, etc. with the returned <code>DocumentReference</code> instance, the provided converter will convert between Firestore data of type <code>NewDbModelType</code> and your custom type <code>NewAppModelType</code>. |
| [withConverter(converter)](./firestore_.documentreference.md#documentreferencewithconverter) | | Removes the current converter. |

@@ -96,6 +99,68 @@ The type of this Firestore reference.
readonly type = "document";
```

## DocumentReference.fromJSON()

Builds a `DocumentReference` instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson)<!-- -->.

<b>Signature:</b>

```typescript
static fromJSON(firestore: Firestore, json: object): DocumentReference;
```

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance the snapshot should be loaded for. |
| json | object | a JSON object represention of a <code>DocumentReference</code> instance |

<b>Returns:</b>

[DocumentReference](./firestore_.documentreference.md#documentreference_class)

an instance of [DocumentReference](./firestore_.documentreference.md#documentreference_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs.

## DocumentReference.fromJSON()

Builds a `DocumentReference` instance from a JSON object created by [DocumentReference.toJSON()](./firestore_.documentreference.md#documentreferencetojson)<!-- -->.

<b>Signature:</b>

```typescript
static fromJSON<NewAppModelType = DocumentData, NewDbModelType extends DocumentData = DocumentData>(firestore: Firestore, json: object, converter: FirestoreDataConverter<NewAppModelType, NewDbModelType>): DocumentReference<NewAppModelType, NewDbModelType>;
```

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| firestore | [Firestore](./firestore_.firestore.md#firestore_class) | The [Firestore](./firestore_.firestore.md#firestore_class) instance the snapshot should be loaded for. |
| json | object | a JSON object represention of a <code>DocumentReference</code> instance |
| converter | [FirestoreDataConverter](./firestore_.firestoredataconverter.md#firestoredataconverter_interface)<!-- -->&lt;NewAppModelType, NewDbModelType&gt; | Converts objects to and from Firestore. |

<b>Returns:</b>

[DocumentReference](./firestore_.documentreference.md#documentreference_class)<!-- -->&lt;NewAppModelType, NewDbModelType&gt;

an instance of [DocumentReference](./firestore_.documentreference.md#documentreference_class) if the JSON object could be parsed. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if an error occurs.

## DocumentReference.toJSON()

Returns a JSON-serializable representation of this `DocumentReference` instance.

<b>Signature:</b>

```typescript
toJSON(): object;
```
<b>Returns:</b>

object

a JSON representation of this object.

## DocumentReference.withConverter()

Applies a custom data converter to this `DocumentReference`<!-- -->, allowing you to use your own custom model objects with Firestore. When you call [setDoc()](./firestore_lite.md#setdoc_ee215ad)<!-- -->, [getDoc()](./firestore_lite.md#getdoc_4569087)<!-- -->, etc. with the returned `DocumentReference` instance, the provided converter will convert between Firestore data of type `NewDbModelType` and your custom type `NewAppModelType`<!-- -->.
16 changes: 16 additions & 0 deletions docs-devsite/firestore_.documentsnapshot.md
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ export declare class DocumentSnapshot<AppModelType = DocumentData, DbModelType e
| [data(options)](./firestore_.documentsnapshot.md#documentsnapshotdata) | | Retrieves all fields in the document as an <code>Object</code>. Returns <code>undefined</code> if the document doesn't exist.<!-- -->By default, <code>serverTimestamp()</code> values that have not yet been set to their final value will be returned as <code>null</code>. You can override this by passing an options object. |
| [exists()](./firestore_.documentsnapshot.md#documentsnapshotexists) | | Returns whether or not the data exists. True if the document exists. |
| [get(fieldPath, options)](./firestore_.documentsnapshot.md#documentsnapshotget) | | Retrieves the field specified by <code>fieldPath</code>. Returns <code>undefined</code> if the document or field doesn't exist.<!-- -->By default, a <code>serverTimestamp()</code> that has not yet been set to its final value will be returned as <code>null</code>. You can override this by passing an options object. |
| [toJSON()](./firestore_.documentsnapshot.md#documentsnapshottojson) | | Returns a JSON-serializable representation of this <code>DocumentSnapshot</code> instance. |

## DocumentSnapshot.(constructor)

@@ -144,3 +145,18 @@ any

The data at the specified field location or undefined if no such field exists in the document.

## DocumentSnapshot.toJSON()

Returns a JSON-serializable representation of this `DocumentSnapshot` instance.

<b>Signature:</b>

```typescript
toJSON(): object;
```
<b>Returns:</b>

object

a JSON representation of this object. Throws a [FirestoreError](./firestore_.firestoreerror.md#firestoreerror_class) if this `DocumentSnapshot` has pending writes.

Loading