Skip to main content

Repositories

Repositories give application code a focused API for one entity. A data repository is an ordinary TypeScript class decorated with the Data package's @Repository decorator and derived from BaseRepository.

Define an entity​

Entities use TypeORM decorators. For a relational database:

entities/user.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;

@Column({ unique: true })
email: string;

@Column()
displayName: string;

@Column()
status: string;
}

For MongoDB, use TypeORM's Mongo entity conventions, including @ObjectIdColumn() for the document identifier. See TypeORM MongoDB entities.

Create a repository​

repositories/user.repository.ts
import { Repository, BaseRepository } from '@axisparkjs/data';
import { DataSource, Repository as TypeOrmRepository } from 'typeorm';
import { User } from '../entities/user';

@Repository({ entity: User })
export class UserRepository extends BaseRepository<User> {
constructor(repository: TypeOrmRepository<User>, dataSource: DataSource) {
super(repository, dataSource);
}

declare findByEmail: (email: string) => Promise<User[]>;
declare findOneByEmail: (email: string) => Promise<User | null>;
declare countByStatus: (status: string) => Promise<number>;
}

The decorator both records which entity the repository serves and marks the class for dependency injection. The plugin creates its implementation at startup and supplies the TypeORM repository and owning data source to the constructor. The TypeORM repository is selected automatically; for MongoDB the generator supplies MongoRepository.

Use declare for derived method signatures. It gives callers static TypeScript types without emitting a JavaScript method that would shadow the method generated by the repository proxy.

Inject and use a repository​

controllers/users.controller.ts
import { Controller, Get, Query } from '@axisparkjs/http';
import { InjectRepository } from '@axisparkjs/data';
import { UserRepository } from '../repositories/user.repository';

@Controller('users')
export class UsersController {
constructor(@InjectRepository(UserRepository) private readonly users: UserRepository) {}

@Get('search')
search(@Query('email') email: string) {
return this.users.findOneByEmail(email);
}
}

InjectRepository accepts the decorated repository class. For direct access to a named TypeORM source, use InjectDataSource:

import { InjectDataSource } from '@axisparkjs/data';
import { DataSource } from 'typeorm';

constructor(@InjectDataSource('PRIMARY') private readonly dataSource: DataSource) {}

Data source names are looked up case-insensitively for injection tokens (PRIMARY and primary map to the same token). Repository injection uses the repository class name.

BaseRepository methods​

BaseRepository<T> delegates these operations to the TypeORM repository selected for the entity:

MethodBehavior
save(entity) / save(entities)Insert or update one entity or an array.
find(options?)Returns matching entities using TypeORM FindManyOptions<T>.
count(options?)Counts entities matching the options.
exists(options?)Returns whether at least one entity matches the options.
remove(entity) / remove(entities)Removes persisted entities and returns the removed instance or instances.

The options argument follows TypeORM's find options. Use the TypeORM documentation for database-specific behavior and options.

const activeUsers = await this.users.find({ where: { status: 'active' }, order: { email: 'ASC' } });
const total = await this.users.count({ where: { status: 'active' } });
const hasAdmin = await this.users.exists({ where: { status: 'admin' } });

Implementation details​

The generated repository is a JavaScript Proxy. Existing class members and inherited BaseRepository methods are used normally. If a property is not implemented but its name matches one of the supported derived-query prefixes, the proxy returns an executable query function. Other missing properties remain undefined; the proxy deliberately does not expose a synthetic then method, so it is not accidentally treated as a Promise/thenable.