BaseModel
The BaseModel class is the core of Esix ORM. All models in your application should extend this class to gain access to type-safe database operations, query building capabilities, and aggregation functions.
Overview
BaseModel provides both static methods (for querying collections) and instance methods (for manipulating individual model instances). It follows a fluent interface pattern, allowing you to chain multiple query methods together.
Properties
Every model that extends BaseModel has these properties:
Unique identifier for the model instance. Automatically generated if not provided.
Unix timestamp (milliseconds) when the model was created.
updatedAt
number | null
default:"null"
Unix timestamp (milliseconds) when the model was last updated. null if never updated.
Static Methods
Query Methods
all()
Returns all models from the collection.
static async all<T extends BaseModel>(this: ObjectType<T>): Promise<T[]>
Array of all model instances
Example
const posts = await BlogPost.all();
find()
Returns the model with the given id.
static async find<T extends BaseModel>(
this: ObjectType<T>,
id: string
): Promise<T | null>
The unique identifier of the model to find
The model instance if found, otherwise null
Example
const post = await BlogPost.find('5f5a41cc3eb990709eafda43');
findBy()
Returns the first model where the specified key matches the given value.
static async findBy<T extends BaseModel, K extends keyof T | '_id'>(
this: ObjectType<T>,
key: K,
value: K extends keyof T ? T[K] : any
): Promise<T | null>
key
K extends keyof T | '_id'
required
The property name to search by (or ‘_id’ for the database ID)
The value to match against
The first matching model instance, or null if not found
Example
const user = await User.findBy('email', 'john.smith@company.com');
where()
Returns a QueryBuilder where the specified key matches a value or satisfies a comparison.
// Two-parameter syntax (equality)
static where<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
value: any
): QueryBuilder<T>
// Three-parameter syntax (comparison operators)
static where<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
operator: ComparisonOperator,
value: any
): QueryBuilder<T>
key
K extends keyof T
required
The property name to filter by
Comparison operator: '=', '!=', '<>', '>', '>=', '<', '<='
The value to compare against
A QueryBuilder instance for method chaining
Examples
// Equality (two-parameter syntax)
const posts = await BlogPost.where('status', 'published').get();
// Comparison operators (three-parameter syntax)
const adults = await User.where('age', '>=', 18).get();
const youngUsers = await User.where('age', '<', 30).get();
// Chaining multiple conditions
const workingAge = await User
.where('age', '>=', 18)
.where('age', '<=', 65)
.get();
whereIn()
Returns models where the specified key’s value is in the given array.
static whereIn<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
values: any[]
): QueryBuilder<T>
key
K extends keyof T
required
The property name to filter by
Array of values to match against
A QueryBuilder instance for method chaining
Example
const comments = await Comment.whereIn('postId', [1, 2, 3]).get();
whereNotIn()
Returns models where the specified key’s value is not in the given array.
static whereNotIn<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
values: any[]
): QueryBuilder<T>
key
K extends keyof T
required
The property name to filter by
Array of values to exclude
A QueryBuilder instance for method chaining
Example
const users = await User.whereNotIn('id', [1, 2, 3]).get();
orderBy()
Specifies the order in which models are returned.
static orderBy<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
order: 'asc' | 'desc' = 'asc'
): QueryBuilder<T>
key
K extends keyof T
required
The property name to sort by
order
'asc' | 'desc'
default:"'asc'"
Sort order: ascending or descending
A QueryBuilder instance for method chaining
Example
const posts = await BlogPost.orderBy('publishedAt', 'desc').get();
limit()
Limits the number of models returned.
static limit<T extends BaseModel>(
this: ObjectType<T>,
length: number
): QueryBuilder<T>
Maximum number of models to return
A QueryBuilder instance for method chaining
Example
const recentPosts = await BlogPost.orderBy('createdAt', 'desc').limit(5).get();
skip()
Skips the specified number of models. Useful for pagination.
static skip<T extends BaseModel>(
this: ObjectType<T>,
length: number
): QueryBuilder<T>
A QueryBuilder instance for method chaining
Example
// Page 2 with 10 items per page
const page2 = await BlogPost.skip(10).limit(10).get();
Data Manipulation Methods
create()
Creates a new model with the given attributes. The ID will be automatically generated if not provided.
static async create<T extends BaseModel>(
this: ObjectType<T>,
attributes: Partial<T>
): Promise<T>
Object containing the model’s attributes
The newly created model instance
Example
const post = await BlogPost.create({ title: 'My First Blog Post!' });
firstOrCreate()
Finds a model matching the filter, or creates a new one with the given attributes if none exists.
static async firstOrCreate<T extends BaseModel>(
this: ObjectType<T>,
filter: Partial<T>,
attributes?: Partial<T>
): Promise<T>
Object to search for existing model
Attributes to use when creating new model (optional, defaults to filter)
The found or newly created model instance
Examples
// Retrieve flight by name or create it if it doesn't exist
const flight = await Flight.firstOrCreate({
name: 'London to Paris'
});
// Retrieve flight by name or create it with additional attributes
const flight = await Flight.firstOrCreate(
{ name: 'London to Paris' },
{ delayed: 1, arrival_time: '11:30' }
);
Aggregation Methods
count()
Returns the number of documents for this model.
static async count<T extends BaseModel>(
this: ObjectType<T>
): Promise<number>
Example
const userCount = await User.count();
sum()
Returns the sum of all values for the given key.
static async sum<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
key
K extends keyof T
required
The property name to sum
Example
const totalSales = await Order.sum('amount');
average()
Returns the average of all values for the given key.
static async average<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
key
K extends keyof T
required
The property name to average
Example
const avgAge = await User.average('age');
min()
Returns the smallest value for the given key.
static async min<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
key
K extends keyof T
required
The property name to find minimum value
Example
const minAge = await User.min('age');
max()
Returns the largest value for the given key.
static async max<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<number>
key
K extends keyof T
required
The property name to find maximum value
Example
const maxScore = await Test.max('score');
percentile()
Returns the nth percentile of all values for the given key.
static async percentile<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K,
n: number
): Promise<number>
key
K extends keyof T
required
The property name to calculate percentile
The percentile to calculate (0-100)
Examples
const median = await ResponseTime.percentile('value', 50);
const p95 = await ResponseTime.percentile('value', 95);
pluck()
Returns an array of values for the given key.
static pluck<T extends BaseModel, K extends keyof T>(
this: ObjectType<T>,
key: K
): Promise<T[K][]>
key
K extends keyof T
required
The property name to extract values from
Array of values for the specified key
Example
const titles = await BlogPost.pluck('title');
// => ['First Post', 'Second Post', 'Third Post']
aggregate()
Provides direct access to MongoDB’s aggregation pipeline.
static async aggregate<T extends BaseModel>(
this: ObjectType<T>,
stages: Record<string, unknown>[]
)
stages
Record<string, unknown>[]
required
Array of MongoDB aggregation pipeline stages
The result of the aggregation
Example
const results = await User.aggregate([
{ $group: { _id: '$department', count: { $sum: 1 } } }
]);
Instance Methods
save()
Persists the current changes to the database. If the model doesn’t have an ID, it creates a new document. Otherwise, it updates the existing document.
async save(): Promise<void>
Promise that resolves when the save is complete
Example
const post = new Post();
post.title = 'My Second Blog Post!';
await post.save();
delete()
Deletes the model from the database.
async delete(): Promise<number>
Number of documents deleted (should be 1 for instance methods)
Example
hasMany()
Defines a one-to-many relationship between models.
hasMany<T extends BaseModel>(
ctor: ObjectType<T>,
foreignKey?: string,
localKey?: string
): QueryBuilder<T>
The related model class constructor
The foreign key field name (defaults to camelCase of parent model name + “Id”)
A QueryBuilder for the related models
Example
const user = await User.find('123');
const posts = await user.hasMany(Post).get();
Type Safety
BaseModel leverages TypeScript’s advanced type system to provide complete type safety:
- Generic constraints:
<T extends BaseModel> ensures type consistency
- Keyof operator:
K extends keyof T restricts field names to actual model properties
- Conditional types: Return types adapt based on input parameters
- Constructor typing:
ObjectType<T> enables proper static method typing
Example with custom model
class User extends BaseModel {
email: string = '';
age: number = 0;
status: 'active' | 'inactive' = 'active';
}
// TypeScript knows these are valid
const user = await User.findBy('email', 'test@example.com');
const adults = await User.where('age', '>=', 18).get();
// TypeScript will error on invalid property names
const invalid = await User.findBy('invalidField', 'value'); // Error!