# new BaseRepository()
Base repository class providing data access patterns for all domain repositories.
Handles the translation between database rows (via Objection.js) and immutable Entity instances. All domain repositories (IconRepository, UserRepository, etc.) extend this class and inherit these CRUD operations.
Responsibilities:
- Execute database queries via Objection.js models
- Convert query results to Entity instances
- Enforce immutability (freeze entities)
- Support transactions
- Provide consistent CRUD interface
Does NOT:
- Contain business logic (use Services)
- Handle caching (use Services with CacheableService mixin)
- Emit events (use Services)
- Enforce access control (use Services with AccessControl)
Examples
// Define domain repository
class IconRepository extends BaseRepository {
constructor(opts = {}) {
super({
modelName: 'IconModel',
entityClass: IconEntity,
hooks: {
afterFind: async (icon) => {
// Post-process icon if needed
return icon;
}
},
...opts
});
}
// Custom query
async findActiveBySetId(setId) {
const rows = await this.model.query()
.where({ set_id: setId, is_active: true })
.orderBy('name');
return this.wrapEntity(rows, this.entityClass);
}
}
// Use in service
const repository = new IconRepository();
const icon = await repository.findById(123);
console.log(icon instanceof IconEntity); // true
console.log(Object.isFrozen(icon)); // true
Methods
# async applyHook(name, value) → {Promise.<(Object|Array)>}
Applies a hook to the given value
Parameters:
| Name | Type | Description |
|---|---|---|
name |
String
|
The name of the hook to apply |
value |
Object
|
Array
|
The value to apply the hook to |
- The value after applying the hook
Promise.<(Object|Array)>
# async count()
Counts the number of records that match the given criteria
# async cursorPage()
Cursor-based pagination (stub).
# async deleteWhere()
Deletes records that match the given criteria
# async exists()
Checks if a record exists that matches the given criteria
# async findAll()
Finds all records that match the given criteria
# async findOne()
Finds a record matching a where clause
# async findOneWithRelations()
Finds a single record with related data (no afterFind hook to avoid relation mutations)
# async paginate(whereopt, pageopt, pageSizeopt, optionsopt) → {Promise.<Object>}
Paginate query results with offset-based pagination.
Returns paginated results with metadata (total count, page info). Uses Objection's .page() method which efficiently counts total rows and fetches the requested page.
Note: Offset pagination doesn't scale well beyond ~100K rows. For large datasets, consider cursor-based pagination (currently not implemented).
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
where |
Object
|
<optional> |
{} | WHERE clause conditions |
page |
number
|
<optional> |
1 | Page number (1-indexed) |
pageSize |
number
|
<optional> |
10 | Items per page |
options |
Object
|
<optional> |
{} | Query options |
entityClass |
function
|
<optional> |
Entity class override |
|
entityOptions |
Object
|
<optional> |
Entity constructor options |
|
trx |
Object
|
<optional> |
Knex transaction object |
Pagination result:
- results: Array of Entity instances
- total: Total row count
- page: Current page (1-indexed)
- pageSize: Items per page
- totalPages: Total page count
Promise.<Object>
Example
const result = await iconRepo.paginate(
{ is_active: true },
1, // page
20 // pageSize
);
console.log(result.results.length); // 20
console.log(result.total); // 150000
console.log(result.totalPages); // 7500
# async update()
Updates a record by its ID NOTE: mirrors your existing behavior (returns patch result wrapped).
# async updateWhere()
Updates records that match the given criteria
# async upsert()
Upserts a record (insert or update)
# async withRelations()
Finds records with related data (no afterList hook to avoid relation mutations)
# wrapEntity(result, entityClass, entityOptionsopt) → {Object|Array.<Object>}
Convert database rows to Entity instances.
This is the core method that transforms plain database objects (from Objection.js) into immutable, frozen Entity instances. Handles both single objects and arrays.
Entity Wrapping Process:
- Convert Objection model instance to plain object (via toJSON if available)
- Pass to Entity constructor
- Entity filters hidden fields, materializes relations
- Returns frozen, immutable Entity instance
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
result |
Object
|
Array.<Object>
|
Database row(s) from Objection query |
||
entityClass |
function
|
Entity class to instantiate (e.g., IconEntity) |
||
entityOptions |
Object
|
<optional> |
{} | Options passed to Entity constructor |
includeHiddenFields |
boolean
|
<optional> |
Include hidden fields |
Entity instance(s)
Object
|
Array.<Object>
Examples
// Single entity
const row = await IconModel.query().findById(1);
const icon = this.wrapEntity(row, IconEntity);
console.log(icon instanceof IconEntity); // true
// Array of entities
const rows = await IconModel.query().where({ is_active: true });
const icons = this.wrapEntity(rows, IconEntity);
console.log(icons[0] instanceof IconEntity); // true