Skip to main content

Repository & RepoUtils

All good database abstraction layers offer easy to use wrappers that provide object-to-relational mapping and access to the underlying database operations. RapidREST is no different. The @Repository decorator injects a Repository (TypeORM) or MongoRepository wrapper into your code, giving you direct access to the database for the data model in question. However, these low-level wrappers don't do much besides call the underlying database driver functions.

In addition to these basic ORM wrappers, RapidREST offers a powerful utility class called RepoUtils that adds the business logic every REST API needs: caching, validation, permissions, optimistic locking, filtering, and more. RepoUtils is the true foundation of RapidREST's persistence engine, used throughout the entire framework. It's the exact same class ModelRoute itself is built on to power every CRUD endpoint.

@Repository

@Repository(type, required?) is the decorator that gets you the raw wrapper. It resolves to a TypeORM Repository for a SQL-backed entity, or a MongoRepository for a Mongo-backed one, whichever @DataStore on the entity points at:

import { DatabaseDecorators } from '@rapidrest/service-core';
import type { MongoRepository } from '@rapidrest/service-core';
const { Repository } = DatabaseDecorators;

export class PetService {
@Repository(Pet)
private repo!: MongoRepository<Pet>;
}

required (default true) throws at startup if the entity's datastore isn't configured, rather than leaving the property silently undefined.

This is genuinely raw: no ACL check, no cache, no change notification. Reach for it when you need a query shape RepoUtils's filter language can't express, or direct access to something ORM-specific, a TypeORM query builder, a Mongo aggregation pipeline, and apply your own permission checks if the result is ever exposed to a caller.

RepoUtils

RepoUtils<T> is what fills that gap: find/create/update/delete for one entity type, with ACL checks, response caching, and change notifications already wired in, the same behavior Auto CRUD Routes already gives you, available directly in your own code:

import { ObjectDecorators } from '@rapidrest/core';
import { RepoUtils } from '@rapidrest/service-core';
const { Inject } = ObjectDecorators;

export class PetService {
@Inject(RepoUtils, { name: Pet.name, args: [Pet] })
private petUtils?: RepoUtils<Pet>;

async adopt(id: string, user?: JWTUser) {
const existing = await this.petUtils?.findOne(id, { user });
if (!existing) return undefined;

return this.petUtils?.update({ uid: id, adopted: true }, existing, { user });
}
}

The methods worth knowing:

MethodWhat it does
find(query, options?)Returns matching documents, same filter language as Filtering, Pagination & Sorting
findOne(id, options?)Returns a single document by id, or undefined
count(query, options?)Counts matching documents
exists(id, options?)Whether a document with the given id exists
create(obj, options?)Creates a document, running the same validation a CRUD endpoint would
update(obj, existing, options?)Updates a document. obj is the partial patch, existing is the current record, fetch it with findOne first
delete(uid, options)Deletes (or soft-deletes) a document
truncate(query, options)Deletes every document matching the query

Every one of these takes an options object that carries the acting user (for the ACL check), among other things, pass it through, the same way a route handler would. Skip it and the call runs as if no one were authenticated, which usually means it gets rejected once ACLs are enabled.

Which one to use

  • Building a service that does what a CRUD endpoint does, list/get/create/update/delete for one model, permission-checked, cached: use RepoUtils.
  • Running a query RepoUtils's filter language can't express, or calling something ORM-specific (a TypeORM query builder, a Mongo aggregation pipeline): use @Repository, and apply your own permission checks if the result is ever exposed to a caller.
  • Need both in the same service? Inject both, they're not mutually exclusive, RepoUtils for the common path, @Repository for the one query that needs it.