Skip to main content

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:
id
string
default:"''"
Unique identifier for the model instance. Automatically generated if not provided.
createdAt
number
default:"0"
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[]>
returns
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>
id
string
required
The unique identifier of the model to find
returns
Promise<T | null>
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)
value
any
required
The value to match against
returns
Promise<T | null>
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
operator
ComparisonOperator
Comparison operator: '=', '!=', '<>', '>', '>=', '<', '<='
value
any
required
The value to compare against
returns
QueryBuilder<T>
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
values
any[]
required
Array of values to match against
returns
QueryBuilder<T>
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
values
any[]
required
Array of values to exclude
returns
QueryBuilder<T>
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
returns
QueryBuilder<T>
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>
length
number
required
Maximum number of models to return
returns
QueryBuilder<T>
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>
length
number
required
Number of models to skip
returns
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>
attributes
Partial<T>
required
Object containing the model’s attributes
returns
Promise<T>
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>
filter
Partial<T>
required
Object to search for existing model
attributes
Partial<T>
Attributes to use when creating new model (optional, defaults to filter)
returns
Promise<T>
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>
returns
Promise<number>
Total count of documents
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
returns
Promise<number>
Sum of all values
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
returns
Promise<number>
Average of all values
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
returns
Promise<number>
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
returns
Promise<number>
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
n
number
required
The percentile to calculate (0-100)
returns
Promise<number>
The nth percentile value
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
returns
Promise<T[K][]>
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
returns
Promise<any>
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>
returns
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>
returns
Promise<number>
Number of documents deleted (should be 1 for instance methods)
Example
await post.delete();

hasMany()

Defines a one-to-many relationship between models.
hasMany<T extends BaseModel>(
  ctor: ObjectType<T>,
  foreignKey?: string,
  localKey?: string
): QueryBuilder<T>
ctor
ObjectType<T>
required
The related model class constructor
foreignKey
string
The foreign key field name (defaults to camelCase of parent model name + “Id”)
localKey
string
default:"'id'"
The local key field name
returns
QueryBuilder<T>
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!