@nathapp/nestjs-data
v3.3.0
Published
ORM-agnostic data access abstractions for NestJS
Readme
@nathapp/nestjs-data
ORM-agnostic data access abstractions — a portable repository contract and transaction-manager interface, with no NestJS or ORM runtime imports.
Installation
npm install @nathapp/nestjs-dataThis is a peer-layer library in the @nathapp/nestjs-* stack: it defines the
contract, not the implementation. Concrete implementations live in
@nathapp/nestjs-prisma (AbstractPrismaRepository) and
@nathapp/nestjs-typeorm (AbstractTypeOrmRepository).
What it provides
IRepository<TDomain, TId>— portable repository contract:findById,findOne,findAll(paginated),count,create,update,delete. Services depend on this interface via a DI token instead of a concrete ORM repo class.IPageResult<T>— ORM-agnostic paginated result shape (total,current,size,hasNext,hasPrev,records), structurally compatible withPage<T>from@nathapp/nestjs-common.ITransactionManager— ORM-agnostic transaction control built onAsyncLocalStorage:run(fn),getClient<C>(),isInTransaction().TRANSACTION_MANAGER— global DI token (Symbol.for('TRANSACTION_MANAGER')) for the default connection'sITransactionManager. Multi-DB apps must use named tokens instead (see the concrete ORM package's docs).
Usage
Services depend on IRepository<TDomain> and ITransactionManager via DI
tokens, never on a concrete ORM class:
import { Inject, Injectable } from '@nestjs/common';
import { IRepository, ITransactionManager, TRANSACTION_MANAGER } from '@nathapp/nestjs-data';
export const USER_REPOSITORY = Symbol('USER_REPOSITORY');
interface User {
id: string;
email: string;
}
@Injectable()
export class UserService {
constructor(
@Inject(USER_REPOSITORY) private readonly users: IRepository<User>,
@Inject(TRANSACTION_MANAGER) private readonly tx: ITransactionManager,
) {}
async register(input: User) {
return this.tx.run(async () => {
const existing = await this.users.findOne({ email: input.email });
if (existing) throw new Error('email already registered');
return this.users.create(input);
});
}
async list(page: { current: number; size: number }) {
return this.users.findAll(page); // -> IPageResult<User>
}
}Bind USER_REPOSITORY to a concrete implementation (e.g.
AbstractPrismaRepository subclass from @nathapp/nestjs-prisma) in your
module's providers. For ORM-specific power (Prisma include, TypeORM
QueryBuilder, raw SQL), inject the concrete repo class directly — those call
sites intentionally give up portability.
Limitations
Concrete base classes structurally assume the primary key property is named
"id". Schemas using a different PK name must implement IRepository
directly rather than extending the abstract base class.
