Repository: CRUD and Finders
Repository: CRUD and Finders
Relevant source files
The following files were used as context for generating this wiki page:
The Repository class is the central engine for data access in the Primebrick DAL. It provides a type-safe, high-level API for interacting with PostgreSQL tables defined via Entity decorators. While the Dal class manages connection pools and global configuration, it delegates all specific data operations—finding, writing, and bulk processing—to the Repository [src/dal/dal.ts:208-212, src/repository/repository.ts:148-150].
Core Abstractions
The Repository operates on a Queryable abstraction, which allows it to run queries against either a standard connection pool (pg.Pool) or a specific client within a transaction (pg.PoolClient) [src/repository/repository.ts:43-43].
| Component | Responsibility |
|---|---|
| Delegation | The Dal instance provides helper methods (e.g., dal.repo.find) that proxy calls to an internal Repository instance [src/dal/dal.ts:208-220]. |
| Metadata Integration | Uses getEntityPersistenceMeta to map class properties to SQL columns, identifying Primary Keys and auditable fields at runtime [src/repository/repository.ts:159-161]. |
| Query Building | Leverages the Query DSL and buildSelectQuery to generate parameterized SQL, ensuring protection against SQL injection [src/repository/repository.ts:163-167]. |
System Architecture: From Dal to Database
The following diagram illustrates how the Repository acts as the bridge between the high-level Dal gateway and the low-level SQL execution.
Data Flow Architecture
Code
Sources: src/dal/dal.ts:208-220, src/repository/repository.ts:148-150, src/repository/repository.ts:163-169, src/meta/entity-meta.ts:114-118
Read Operations
Read operations are designed around the "Fail Fast" principle. By default, single-row finders like findById and findByUUID will throw a NotFoundError if the record is missing, though this can be toggled via FindOptions [src/repository/repository.ts:158-175].
Key capabilities include:
- Filters and Joins: Full support for the Query DSL for complex WHERE clauses and relations.
- Soft-Delete Awareness: Automatically filters out records where
deleted_atis set, unlessdeletedRecords: 'INCLUDE'is specified [src/repository/repository.ts:166-166]. - Streaming: For large datasets,
findAllcan return anAsyncIterableusingpg-query-streamto prevent memory exhaustion [src/repository/repository.ts:248-255].
For details, see Read Operations.
Write Operations
Write operations handle the lifecycle of a single record, including automatic audit stamping and versioning. The DAL follows a strict "RETURNING *" policy, ensuring that the JavaScript object returned by an add or update call perfectly reflects the state of the database (including serial IDs and default values) [src/repository/repository.ts:360-365].
Key capabilities include:
- Audit Stamping: Automatically populates
created_by,updated_by, anddeleted_atbased on the providedactorId[src/repository/repository.ts:333-350]. - Upsert: Uses PostgreSQL
INSERT ... ON CONFLICTlogic to handle atomic "create or update" scenarios [src/repository/repository.ts:391-410]. - Soft Delete: The
deletemethod performs anUPDATEto set a deletion timestamp rather than a physicalDELETE, preserving data for the audit trail [src/repository/repository.ts:517-530].
For details, see Write Operations.
Bulk Operations
For high-performance scenarios, the Repository provides optimized methods for handling thousands of rows. These methods bypass the overhead of single-row processing while maintaining safety limits [src/repository/repository.ts:634-640].
Key capabilities include:
- Parameter Limit Protection:
addManyautomatically batches inserts to stay under the PostgreSQL limit of 65,535 parameters [src/repository/repository.ts:142-146]. - Temp Table Strategy:
updateManyutilizes aCREATE TEMP TABLE ... ON COMMIT DROPpattern. Data is batched into the temp table, followed by a single bulkUPDATEjoin, significantly reducing round-trips [src/repository/repository.ts:771-790].
For details, see Bulk Operations.
Entity to SQL Mapping
The Repository uses the EntityPersistenceMeta to translate between the "Natural Language Space" of TypeScript classes and the "Code Entity Space" of the database schema.
Mapping Logic
Code
Sources: src/meta/entity-meta.ts:153-162, src/meta/entity-meta.ts:175-182, src/repository/repository.ts:5-11
Error Hierarchy
The repository communicates state via a specific error hierarchy defined in src/errors/errors.ts. This allows consumers to distinguish between "Record Not Found" and "Database Connection Timeout" without parsing string messages [src/repository/repository.ts:40-40].
| Error Class | Trigger |
|---|---|
NotFoundError | Thrown by finders when throwIfNotFound is true and 0 rows return [src/errors/errors.ts:16-20]. |
MultipleRowsError | Thrown when a single-row finder finds >1 record [src/errors/errors.ts:22-26]. |
ValidationError | Thrown when required fields (like matchBy values) are missing [src/errors/errors.ts:34-38]. |
UnknownColumnError | Thrown if a query refers to a property not decorated with @Column [src/errors/errors.ts:28-32]. |
Sources: src/repository/repository.ts:40-40, src/errors/errors.ts:1-40