Documentation
Define Fireclass models and understand their create, read, update, delete, and hydration behavior.
A Fireclass model is a decorated class that extends the BaseModel bound to
your Firebase runtime. The model owns field types and validation rules;
BaseModel supplies document persistence and typed static queries.
Import BaseModel from your initialized local Fireclass entry. Import
decorators directly from the runtime SDK.
import { Collection } from "@dharayush7/fireclass-js";
import { IsEmail, IsInt, IsString, Min } from "class-validator";
import { BaseModel } from "../lib/fireclass.js";
@Collection("users")
export class User extends BaseModel<User> {
@IsString()
name!: string;
@IsEmail()
email!: string;
@IsInt()
@Min(0)
age!: number;
constructor(data?: Partial<User>) {
super(data);
Object.assign(this, data);
}
}The generic parameter keeps query fields and values tied to the model. The constructor accepts partial data because Fireclass uses it for both new and hydrated instances.
Every model instance has an optional id:
const user = new User({
name: "Ada Lovelace",
email: "ada@example.com",
age: 36,
});
user.id; // undefined
await user.save();
user.id; // generated Firestore document idFireclass never writes id into the document body. It comes from the Firestore
document snapshot and is attached to hydrated model instances.
| Method | Result | Behavior |
|---|---|---|
instance.save() | Promise<string> | Validates, then creates or merge-updates a document. |
instance.delete() | Promise<void> | Deletes the document identified by instance.id. |
Model.findById(id) | Promise<Model | null> | Reads one document by id. |
Model.findMany(query?) | Promise<Model[]> | Runs a typed query and hydrates every result. |
Model.findOne(query?) | Promise<Model | null> | Runs the query with limit: 1. |
Model.count(query?) | Promise<number> | Uses the adapter's count capability. |
Model.deleteById(id) | Promise<Model | null> | Reads, deletes, and returns the deleted document. |
Model.deleteMany(query?) | Promise<Model[]> | Reads matching documents, deletes them, and returns them. |
save() chooses the write operation from the presence of id.
const user = new User({
name: "Ada Lovelace",
email: "ada@example.com",
age: 36,
});
const id = await user.save(); // create
user.age = 37;
await user.save(); // merge update using the same idBefore either write, Fireclass validates the complete model instance. On an update, the adapter writes with Firestore merge semantics, so fields not present in the serialized instance remain unchanged.
Only own enumerable properties are serialized. Methods, static values, and the
id property are excluded.
Merge write, full validation
An update is a merge write, but validation still evaluates the complete model.
Constructing an instance with only an id and one changed field can fail
required-field validation. Read the document first, change it, then save it.
Read methods return instances of your model, not plain objects.
const user = await User.findById("user_123");
if (user) {
user instanceof User; // true
user.id; // "user_123"
await user.save();
}The active adapter may normalize Firestore values before construction. The
built-in server and client adapters recursively convert Firestore Timestamp-like
objects with toDate() into JavaScript Date values.
Reads do not run class-validator. Stored documents are hydrated as they exist; validation runs when an instance is saved.
const adults = await User.findMany({
where: {
age: { gte: 18 },
},
orderBy: {
age: "desc",
},
limit: 20,
});
const ada = await User.findOne({
where: {
email: { equals: "ada@example.com" },
},
});
const adultCount = await User.count({
where: {
age: { gte: 18 },
},
});findOne() always replaces the supplied limit with 1. See Query
Options for operators, ordering, and cursor behavior.
const user = await User.findById("user_123");
await user?.delete();
const deleted = await User.deleteById("user_456");
// deleted is the hydrated pre-delete document, or null when it did not exist.
const removed = await User.deleteMany({
where: {
age: { lt: 13 },
},
});deleteById() and deleteMany() read documents before deleting so they can
return hydrated instances. deleteMany() uses the adapter's batch operation
when available and falls back to sequential deletes otherwise.
Deleting an instance does not clear its in-memory id. Calling save() on that
same instance afterward performs a merge set at the previous document id.
The model API is shared by every runtime package. Only the initialized
BaseModel source changes:
| Runtime | Initialize with | Decorators imported from |
|---|---|---|
| Node / Express | createFireclass(getDb()) | @dharayush7/fireclass-js |
| Next.js | getFireclass() | @dharayush7/fireclass-ssr |
| React | createFireclass(db) | @dharayush7/fireclass-react |
The local Fireclass entry should export initialized values such as BaseModel
and adapter. It should not re-export decorators or standalone SDK helpers.