Repository and Query DSL
Repository and Query DSL
Relevant source files
The following files were used as context for generating this wiki page:
- src/db/pool.ts
- src/db/repository/README.md
- src/db/repository/audit-join-helper.ts
- src/db/repository/auditable-joins.ts
- src/db/repository/auditable-types.ts
- src/db/repository/dsl.ts
- src/db/repository/query-builder.ts
- src/db/repository/repository.ts
- src/db/repository/types.ts
- src/lib/audit/delta-calculator.ts
The Primebrick backend utilizes a generic Repository pattern combined with a type-safe Domain Specific Language (DSL) for SQL query construction. This system abstracts raw PostgreSQL interactions into a structured, entity-aware API that handles filtering, sorting, joining, and automatic audit logging.
Repository Class
The Repository class src/db/repository/repository.ts:27 is the primary interface for data access. It accepts a PostgreSQL Queryable (Pool or Client) and an optional AuditService. It translates high-level DSL expressions into optimized SQL via the query-builder.
Key Methods
insertMany: Performs bulk inserts using a singleINSERT ... VALUESstatement src/db/repository/repository.ts:45-114. It automatically skips identity columns and triggers audit logging for auditable entities.findById: Fetches a single record by its primary key src/db/repository/repository.ts:116-140.findByPage: Provides structured pagination, returning both the entity list and a total record count src/db/repository/repository.ts:177-209.update: Handles updates with optimistic concurrency control. It checks theversioncolumn to ensure no mid-air collisions occur src/db/repository/repository.ts:241-322.
Type-Safe Query DSL
The DSL allows developers to construct queries using TypeScript objects instead of raw strings, ensuring that field names correspond to actual entity properties.
DSL Components
| Component | Function | Description |
|---|---|---|
field | field(Entity, "prop") | References a specific property on an entity src/db/repository/dsl.ts:28-33. |
Filter | Filter.fieldValue(...) | Defines WHERE clauses (supports =, ILIKE, IN, BETWEEN, etc.) src/db/repository/dsl.ts:63-86. |
Sort | Sort.by(...) | Defines ORDER BY clauses src/db/repository/dsl.ts:89-93. |
Join | Join.on(...) | Defines JOIN logic between two entities src/db/repository/dsl.ts:102-111. |
Project | Project.field(...) | Defines specific columns to return in the SELECT clause src/db/repository/dsl.ts:117-124. |
Sources: src/db/repository/repository.ts:27-28, src/db/repository/dsl.ts:3-21, src/db/repository/dsl.ts:35-61
Query Execution Flow
The following diagram illustrates how a DSL request is transformed into a database result.
DSL to SQL Execution Flow
Code
Sources: src/db/repository/query-builder.ts:126-155, src/db/repository/query-builder.ts:156-187, src/db/repository/repository.ts:160-175
Auditable Joins and Display Names
Entities implementing IAuditableEntity store user UUIDs in fields like created_by and updated_by. To resolve these to human-readable names, the system uses "Auditable Joins".
- Guardrail Pattern: Joins to
user_profilesuse a regex check~ '^[0-9a-fA-F-]{36}$'to ensure the field contains a valid UUID before attempting a cast, preventing errors when fields contain strings like "system" src/db/repository/auditable-joins.ts:9-11. - Automatic Projection: The
query-builderautomatically maps join aliases (creator,updater,deleter) to virtual fields likecreated_by_namesrc/db/repository/query-builder.ts:85-102. - Utility Types:
WithAuditableDisplayNames<T>is used to type the resulting rows including these virtual name fields src/db/repository/auditable-types.ts:23-27.
Sources: src/db/repository/auditable-joins.ts:15-36, src/db/repository/README.md:64-83, src/db/repository/audit-join-helper.ts:13-19
Optimistic Concurrency and Audit Logging
The repository enforces data integrity and traceability during updates through the version column and a delta calculator.
Update and Audit Sequence
Code
Sources: src/db/repository/repository.ts:241-322, src/lib/audit/delta-calculator.ts:1-9, src/db/repository/repository.ts:91-113
Delta Calculator
The calculateDelta function compares two objects and returns only the changed fields src/lib/audit/delta-calculator.ts:1-9.
calculateDeltaWithForcedFields: Allows specific fields (likeupdated_by) to be included in the audit log even if their value hasn't changed, ensuring context is preserved src/lib/audit/delta-calculator.ts:16-28.
Soft Deletes and Pagination
- Soft Delete: The
query-builderautomatically appendsWHERE deleted_at IS NULLunless thedeletedRecordsoption is set toINCLUDEDorONLYsrc/db/repository/query-builder.ts:188-198. - Pagination:
findByPageexecutes two queries: one for the total count and one for the limited result set, returning aPaginatedEntity<T>object src/db/repository/repository.ts:177-209.
Sources: src/db/repository/types.ts:1-21, src/db/repository/query-builder.ts:45-48