@typed-firestore/server
Version:
Elegant, typed abstractions for Firestore in server environments
113 lines (96 loc) • 3.28 kB
text/typescript
import type {
CollectionGroup,
CollectionReference,
DocumentData,
QueryDocumentSnapshot,
Transaction,
} from "firebase-admin/firestore";
import { makeMutableDocument, makeMutableDocumentTx } from "~/documents";
import type { FsMutableDocument, FsMutableDocumentTx } from "~/types";
import { invariant, isDefined } from "~/utils";
import { type GetDocumentsOptions } from "./get-documents";
import { getQueryInfo } from "./helpers";
import type { QueryBuilder, SelectedDocument } from "./types";
export async function getFirstDocument<
T extends DocumentData,
S extends (keyof T)[] | undefined = undefined,
>(
ref: CollectionReference<T> | CollectionGroup<T>,
queryFn: QueryBuilder,
options: GetDocumentsOptions<T, S> = {}
): Promise<FsMutableDocument<SelectedDocument<T, S>, T> | undefined> {
const queryInfo = getQueryInfo(queryFn(ref));
const { limit, select: querySelect } = queryInfo;
invariant(
!isDefined(limit),
`You should not set a limit when calling getFirstDocument. It returns only one document.`
);
invariant(
!querySelect,
"Select is not allowed to be set on the query. Use the options instead."
);
const query = options.select
? queryFn(ref).select(...(options.select as string[]))
: queryFn(ref);
const snapshot = await query.limit(1).get();
if (snapshot.empty) {
return;
}
return makeMutableDocument<SelectedDocument<T, S>, T>(
snapshot.docs[0] as QueryDocumentSnapshot<SelectedDocument<T, S>>
);
}
export async function getFirstDocumentData<
T extends DocumentData,
S extends (keyof T)[] | undefined = undefined,
>(
ref: CollectionReference<T> | CollectionGroup<T>,
queryFn: QueryBuilder,
options: GetDocumentsOptions<T, S> = {}
): Promise<T | undefined> {
const document = await getFirstDocument(ref, queryFn, options);
return document?.data;
}
export async function getFirstDocumentTx<
T extends DocumentData,
S extends (keyof T)[] | undefined = undefined,
>(
tx: Transaction,
ref: CollectionReference<T> | CollectionGroup<T>,
queryFn: QueryBuilder,
options: GetDocumentsOptions<T, S> = {}
): Promise<FsMutableDocumentTx<SelectedDocument<T, S>, T> | undefined> {
const queryInfo = getQueryInfo(queryFn(ref));
const { limit, select: querySelect } = queryInfo;
invariant(
!isDefined(limit),
`You should not set a limit when calling getFirstDocument. It returns only one document.`
);
invariant(
!querySelect,
"Select is not allowed to be set on the query. Use the options instead."
);
const query = options.select
? queryFn(ref).select(...(options.select as string[]))
: queryFn(ref);
const snapshot = await tx.get(query.limit(1));
if (snapshot.empty) {
return;
}
return makeMutableDocumentTx<SelectedDocument<T, S>, T>(
tx,
snapshot.docs[0] as QueryDocumentSnapshot<SelectedDocument<T, S>>
);
}
export async function getFirstDocumentDataTx<
T extends DocumentData,
S extends (keyof T)[] | undefined = undefined,
>(
tx: Transaction,
ref: CollectionReference<T> | CollectionGroup<T>,
queryFn: QueryBuilder,
options: GetDocumentsOptions<T, S> = {}
): Promise<T | undefined> {
const document = await getFirstDocumentTx(tx, ref, queryFn, options);
return document?.data;
}