Documentation
Complete reference for the runtime-neutral Fireclass model, query, validation, adapter, and error APIs.
@dharayush7/fireclass-core is the runtime-neutral modeling package. It does
not import Firebase Admin, the Firebase client SDK, React, Express, or Next.js.
Application code normally imports these exports from its runtime SDK. Import core directly when implementing an adapter or building isolated tests.
import {
Collection,
defineBaseModel,
type FirestoreAdapter,
type QueryOptions,
} from "@dharayush7/fireclass-core";| Group | Exports |
|---|---|
| Model | defineBaseModel, BaseModelClass |
| Decorators | Collection, Subcollection, getCollectionName, CollectionMeta |
| Public queries | QueryOptions, FieldFilter, WhereFilter, OrderBy, Cursor, buildQuerySpec |
| Adapter contract | FirestoreAdapter, QuerySpec, QueryCondition, WhereOp, DocSnapshot |
| Validation and conversion | runValidation, convertFirestoreTypes |
| Errors | FireclassError, MissingCollectionError, MissingIdError, ValidationFailedError, UnsupportedAdapterCapabilityError |
function defineBaseModel(adapter: FirestoreAdapter): BaseModelClass;
type BaseModelClass = ReturnType<typeof defineBaseModel>;Creates an adapter-bound base class. Core intentionally does not export a
global BaseModel value because every model must use the class bound to its
runtime's Firestore instance.
const BaseModel = defineBaseModel(adapter);
@Collection("users")
class User extends BaseModel<User> {
name!: string;
}| Member | Signature | Behavior |
|---|---|---|
id | id?: string | Firestore document id; omitted from stored data. |
constructor | constructor(data?: Partial<T>) | Assigns data and requires collection metadata. |
save | save(): Promise<string> | Validates, creates without an id, or merge-updates with an id. |
delete | delete(): Promise<void> | Deletes by id; throws MissingIdError when absent. |
| Method | Signature | Notes |
|---|---|---|
findById | findById(id): Promise<Model | null> | Hydrates one document. |
findMany | findMany(query?): Promise<Model[]> | Hydrates every matching document. |
findOne | findOne(query?): Promise<Model | null> | Replaces any supplied limit with 1. |
count | count(query?): Promise<number> | Requires adapter.count. |
deleteById | deleteById(id): Promise<Model | null> | Reads, deletes, and returns the previous value. |
deleteMany | deleteMany(query?): Promise<Model[]> | Reads first; uses batch delete or a sequential fallback. |
Hydration calls adapter.convert when available, then constructs the model with
the converted fields and id. Read and delete methods do not run validation.
See Models & BaseModel for lifecycle examples.
function Collection(name: string): ClassDecorator;Stores name as static _collection metadata on the target class.
@Collection("posts")
class Post extends BaseModel<Post> {}function Subcollection(
name: string,
options: { parent: unknown },
): ClassDecorator;Stores _collection and _subcollectionParent metadata.
Metadata only
The parent is reserved for future path resolution. Current model methods query the collection name directly and do not construct nested document paths.
interface CollectionMeta {
_collection: string;
_subcollectionParent?: unknown;
}
function getCollectionName(ctor: unknown): string | undefined;getCollectionName reads decorated collection metadata without throwing.
BaseModel converts a missing value into MissingCollectionError.
interface QueryOptions<T> {
where?: WhereFilter<T>;
orderBy?: OrderBy<T>;
limit?: number;
cursor?: Cursor;
}
type WhereFilter<T> = {
[K in keyof T]?: FieldFilter<T[K]>;
};
type OrderBy<T> = {
[K in keyof T]?: "asc" | "desc";
};
interface Cursor {
startAfter?: unknown[];
}type FieldFilter<V> = {
equals?: V;
gt?: V;
gte?: V;
lt?: V;
lte?: V;
in?: V[];
notIn?: V[];
arrayContains?: V extends readonly (infer U)[] ? U : never;
arrayContainsAny?: V extends readonly (infer U)[] ? U[] : never;
};Every defined operator becomes a query condition. undefined values are
skipped; false, 0, empty strings, and null are preserved. Conditions use
AND semantics. Only the first orderBy property is used.
See Query Options for operator mapping and Firestore limitations.
function buildQuerySpec<T>(query?: QueryOptions<T>): QuerySpec;Purely converts the typed public query into the normalized adapter shape. It does not call an adapter or validate Firestore index and operator rules.
const spec = buildQuerySpec<{ age: number }>({
where: { age: { gte: 18, lt: 65 } },
orderBy: { age: "asc" },
limit: 20,
});type WhereOp =
| "eq"
| "gt"
| "gte"
| "lt"
| "lte"
| "in"
| "not-in"
| "array-contains"
| "array-contains-any";
interface QueryCondition {
field: string;
op: WhereOp;
value: unknown;
}
interface QuerySpec {
where: QueryCondition[];
orderBy?: { field: string; direction: "asc" | "desc" };
limit?: number;
startAfter?: unknown[];
}
interface DocSnapshot {
id: string;
data: Record<string, unknown>;
}These types are the boundary between the core query builder and runtime adapters.
interface FirestoreAdapter {
add(collection: string, data: Record<string, unknown>): Promise<{ id: string }>;
set(
collection: string,
id: string,
data: Record<string, unknown>,
options: { merge: boolean },
): Promise<void>;
get(collection: string, id: string): Promise<Record<string, unknown> | null>;
query(collection: string, spec: QuerySpec): Promise<DocSnapshot[]>;
delete(collection: string, id: string): Promise<void>;
batchDelete?(collection: string, ids: string[]): Promise<void>;
count?(collection: string, spec: QuerySpec): Promise<number>;
subscribe?(
collection: string,
spec: QuerySpec,
callback: (docs: DocSnapshot[]) => void,
): () => void;
convert?(data: Record<string, unknown>): Record<string, unknown>;
}add, set, get, query, and delete are required. deleteMany falls back
when batchDelete is absent; count throws when its capability is absent.
See Adapters and the Testing API.
function runValidation<T extends object>(
ctor: new (...args: any[]) => T,
data: object,
): Promise<void>;Transforms data with plainToInstance, validates it with class-validator, and
throws ValidationFailedError with the raw validation errors. It sets
forbidUnknownValues: false, allowing undecorated models.
BaseModel.save() calls this automatically. Direct calls are intended for
advanced integrations.
function convertFirestoreTypes<V>(value: V): V;Recursively converts arrays and plain objects. Any value with a successful
toDate() method becomes a JavaScript Date; failed conversions preserve the
original value.
class FireclassError extends Error {}
class MissingCollectionError extends FireclassError {}
class MissingIdError extends FireclassError {}
class ValidationFailedError extends FireclassError {
readonly errors: ValidationError[];
}
class UnsupportedAdapterCapabilityError extends FireclassError {}Concrete instances restore their prototype chain and set name to the class
name. Firebase SDK and custom adapter errors are not wrapped.
See Errors for triggers and recovery patterns.