Skip to content

Repositories Overview

Repositories are the data access layer in Ignis - they provide type-safe CRUD operations for your database entities.

Files: packages/core/src/base/repositories/core/*.ts

Quick Start

If you're new to repositories, start here:

typescript
import { DefaultCRUDRepository, repository } from '@venizia/ignis';
import { Todo } from '@/models/todo.model';
import { PostgresDataSource } from '@/datasources/postgres.datasource';

@repository({ model: Todo, dataSource: PostgresDataSource })
export class TodoRepository extends DefaultCRUDRepository<typeof Todo.schema> {
  // That's it! You get: find, findOne, create, updateById, deleteById, etc.
}

Repository Classes

ClassCapabilitiesUse Case
AbstractRepositoryBase class with propertiesExtend for custom repositories
ReadableRepositoryRead-only operationsViews, external tables
PersistableRepositoryRead + Write operationsRarely used directly
DefaultCRUDRepositoryFull CRUD operationsStandard data tables

Most common: Extend DefaultCRUDRepository for standard tables.

Available Methods

Read Operations

MethodDescriptionExample
find(opts)Find multiple recordsrepo.find({ filter: { where: { status: 'active' } } })
find(opts) with rangeFind with pagination rangerepo.find({ filter, options: { shouldQueryRange: true } })
findOne(opts)Find single recordrepo.findOne({ filter: { where: { email } } })
findById(opts)Find by primary keyrepo.findById({ id: '123' })
count(opts)Count matching recordsrepo.count({ where: { status: 'active' } })
existsWith(opts)Check if existsrepo.existsWith({ where: { email } })

Write Operations

MethodDescriptionExample
create(opts)Create single recordrepo.create({ data: { title: 'New' } })
createAll(opts)Create multiple recordsrepo.createAll({ data: [{ title: 'A' }, { title: 'B' }] })
updateById(opts)Update by primary keyrepo.updateById({ id: '123', data: { title: 'Updated' } })
updateAll(opts)Update matching recordsrepo.updateAll({ where: { status: 'draft' }, data: { status: 'published' } })
deleteById(opts)Delete by primary keyrepo.deleteById({ id: '123' })
deleteAll(opts)Delete matching recordsrepo.deleteAll({ where: { status: 'archived' } })

Documentation Sections

This documentation is split into focused guides:

Filter System

Complete reference for querying data - operators, JSON filtering, array operators, default filters, and query patterns.

typescript
// Preview
await repo.find({
  filter: {
    where: {
      status: 'active',
      age: { gte: 18 },
      'metadata.priority': { gte: 3 },
      tags: { contains: ['featured'] }
    },
    order: ['createdAt DESC'],
    limit: 20
  }
});

Relations & Includes

Fetch related data using include for eager loading and nested queries.

typescript
// Preview
await repo.find({
  filter: {
    include: [{
      relation: 'posts',
      scope: { where: { published: true } }
    }]
  }
});

Advanced Features

Transactions, hidden properties, default filter bypass, performance optimization, and type inference.

typescript
// Preview
const tx = await repo.beginTransaction();
try {
  await repo.create({ data, options: { transaction: tx } });
  await tx.commit();
} catch (e) {
  await tx.rollback();
}

Repository Mixins

Composable mixins for repository features - DefaultFilterMixin and FieldsVisibilityMixin.

@repository Decorator

Both model AND dataSource are required for schema auto-discovery:

typescript
// ❌ WRONG - Missing dataSource
@repository({ model: User })
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}

// ❌ WRONG - Missing model
@repository({ dataSource: PostgresDataSource })
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}

// ✅ CORRECT
@repository({ model: User, dataSource: PostgresDataSource })
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}

DataSource is auto-injected - no constructor needed:

typescript
@repository({ model: User, dataSource: PostgresDataSource })
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
  // Custom methods only - no boilerplate!

  async findByEmail(opts: { email: string }) {
    return this.findOne({ filter: { where: { email: opts.email } } });
  }
}

Explicit @inject Pattern

When you need constructor control:

typescript
@repository({ model: User, dataSource: PostgresDataSource })
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
  constructor(
    @inject({ key: 'datasources.PostgresDataSource' })
    dataSource: PostgresDataSource,
  ) {
    super(dataSource);
  }
}

Safety Features

Empty Where Protection

Prevents accidental mass updates/deletes:

typescript
// ❌ Throws error - empty where without force flag
await repo.deleteAll({ where: {} });

// ✅ Explicitly allow with force flag (logs warning)
await repo.deleteAll({ where: {}, options: { force: true } });
Scenarioforce: false (default)force: true
Empty whereThrows errorLogs warning, proceeds
Valid whereExecutes normallyExecutes normally

Quick Reference

Want to...Code
Find all activerepo.find({ filter: { where: { status: 'active' } } })
Find with range inforepo.find({ filter, options: { shouldQueryRange: true } })
Find by IDrepo.findById({ id: '123' })
Find with relationsrepo.find({ filter: { include: [{ relation: 'posts' }] } })
Create onerepo.create({ data: { name: 'John' } })
Update by IDrepo.updateById({ id: '123', data: { name: 'Jane' } })
Delete by IDrepo.deleteById({ id: '123' })
Count matchingrepo.count({ where: { status: 'active' } })
Check existsrepo.existsWith({ where: { email: 'test@example.com' } })

Next Steps

See Also