Repository<T>
Repository<T> is the CRUD interface for an entity — it builds and executes the
SELECT/INSERT/UPDATE/DELETE statements for T using
@sqb/builder under the hood, and converts
rows back into instances of T. See the ORM guides for the
full narrative walkthrough (filters, projection, associations) — this page is the field-by-field
API reference.
Get a Repository from a client or connection — don't construct it yourself:
const repo = client.getRepository(Customer);
// or, inside a transaction/acquired connection:
const repo = connection.getRepository(Customer);
See SqbClient.getRepository() and
SqbConnection.getRepository().
Repository extends strict-typed-events' AsyncEventEmitter (via TypedEventEmitterClass) and
emits:
| Event | Signature | When |
|---|---|---|
execute | (request: QueryRequest) => void | A query is about to run. |
error | (error: Error) => void | An error occurred. |
acquire | (connection: SqbConnection) => Promise<void> | A connection was acquired from the pool to run an operation (only fires when the repository was created from a SqbClient, not from an already-acquired SqbConnection). |
Constructor
new Repository<T>(
entityDef: EntityMetadata,
executor: SqbClient | SqbConnection,
schema?: string,
)
Not part of the public API in practice — always obtain a Repository via
SqbClient.getRepository()/
SqbConnection.getRepository(), which resolve
entityDef for you from a decorated class or a registered entity name.
Properties
| Property | Type | Description |
|---|---|---|
entity | EntityMetadata | The entity metadata this repository was built from. |
type | Type<T> | The entity's constructor (entity.ctor). |
Methods
Every method accepts an options object as its last argument (extending
Repository.CommandOptions) and internally acquires a
connection — reusing one passed via options.connection, or the connection the repository was
already bound to. Methods that can return either a full entity instance or a plain partial object
are overloaded: passing a projection in the options narrows the return type to PartialDTO<T>
(a plain object with only the requested fields); omitting it returns a full T instance.
create()
create(
input: PartialDTO<T>,
options: RequiredSome<Repository.CreateOptions, 'projection'>,
): Promise<PartialDTO<T>>;
create(input: PartialDTO<T>, options?: Repository.CreateOptions): Promise<T>;
Inserts a row, then re-fetches it (by its returned key) and resolves with the created record.
Throws if input is falsy (You must provide values), or if the insert's RETURNING didn't
yield a key (Unable to insert new row).
const customer = await repo.create({ givenName: 'Jane', countryCode: 'US' });
createOnly()
createOnly(
input: PartialDTO<T>,
options?: StrictOmit<Repository.CreateOptions, 'projection'>,
): Promise<any>;
Inserts a row but skips the re-fetch — resolves with the new row's primary-key value (a scalar
for a single-column key, or a Record<string, any> for a composite key), or with whatever the
database returned via RETURNING if the entity has no primary index. Cheaper than create()
when you don't need the row back.
const id = await repo.createOnly({ givenName: 'Jane', countryCode: 'US' });
count()
count(options?: Repository.CountOptions): Promise<number>;
Resolves to the number of records matching options.filter (or the whole table when omitted).
const total = await repo.count({ filter: { active: true } });
exists() / existsOne()
exists(
keyValue: any | Record<string, any>,
options?: Repository.ExistsOptions,
): Promise<boolean>;
existsOne(options?: Repository.ExistsOptions): Promise<boolean>;
exists() checks for a row by primary key (optionally narrowed further with options.filter);
existsOne() checks for any row matching options.filter, with no key involved.
await repo.exists(1);
await repo.existsOne({ filter: { givenName: 'Jane' } });
findById()
findById(
keyValue: any | Record<string, any>,
options: RequiredSome<Repository.FindOptions, 'projection'>,
): Promise<PartialDTO<T> | undefined>;
findById(
keyValue: any | Record<string, any>,
options?: Repository.FindOptions,
): Promise<T | undefined>;
const customer = await repo.findById(1);
const summary = await repo.findById(1, { projection: ['id', 'givenName'] });
findOne()
findOne(
options: RequiredSome<Repository.FindOneOptions, 'projection'>,
): Promise<PartialDTO<T> | undefined>;
findOne(options?: Repository.FindOneOptions): Promise<T | undefined>;
Like findMany() with an implicit limit: 1, but resolves to the single record (or undefined)
instead of an array.
const customer = await repo.findOne({ filter: { givenName: 'Jane' }, sort: ['-id'] });
findMany()
findMany(
options: RequiredSome<Repository.FindManyOptions, 'projection'>,
): Promise<PartialDTO<T>[]>;
findMany(options?: Repository.FindManyOptions): Promise<T[]>;
See Repository.FindManyOptions for the full options
reference (sort, offset, limit, distinct, maxEagerFetch, maxSubQueries,
onTransformRow).
const customers = await repo.findMany({
filter: { active: true },
projection: ['id', 'givenName', 'country'],
sort: ['givenName'],
limit: 20,
offset: 40,
});
update()
update(
keyValue: any | Record<string, any>,
input: PatchDTO<T>,
options: RequiredSome<Repository.UpdateOptions, 'projection'>,
): Promise<PartialDTO<T> | undefined>;
update(
keyValue: any | Record<string, any>,
input: PatchDTO<T>,
options?: Repository.UpdateOptions,
): Promise<T | undefined>;
Updates the row identified by keyValue, then re-fetches and returns it (undefined if no row
matched). Any key fields present in input are stripped before the UPDATE is built — you can't
change the primary key value this way.
const updated = await repo.update(1, { givenName: 'Janet' });
updateOnly()
updateOnly(
keyValue: any | Record<string, any>,
input: PatchDTO<T>,
options?: Repository.UpdateOnlyOptions,
): Promise<boolean>;
Same as update() but skips the re-fetch, resolving to whether a row was actually updated.
const wasUpdated = await repo.updateOnly(1, { givenName: 'Janet' });
updateMany()
updateMany(
input: PartialDTO<T>,
options?: Repository.UpdateManyOptions,
): Promise<number>;
Updates every row matching options.filter, resolving to the number of affected rows. See
Bulk operations and an empty filter below.
const count = await repo.updateMany({ active: false }, { filter: { countryCode: 'XX' } });
delete()
delete(
keyValue: any | Record<string, any>,
options?: Repository.DeleteOptions,
): Promise<boolean>;
Deletes the row identified by keyValue (optionally narrowed further with options.filter),
resolving to whether a row was deleted.
await repo.delete(1);
deleteMany()
deleteMany(options?: Repository.DeleteManyOptions): Promise<number>;
Deletes every row matching options.filter, resolving to the number of deleted rows. See
Bulk operations and an empty filter below.
const deleted = await repo.deleteMany({ filter: { countryCode: 'XX' } });
Bulk operations and an empty filter
deleteMany() and updateMany() act on whatever options.filter matches, exactly like a plain
SQL DELETE/UPDATE without a WHERE clause when no filter is given. Reading
DeleteCommand.execute() and UpdateCommand.execute() directly (in
orm/commands/delete.command.ts and orm/commands/update.command.ts) confirms both explicitly
allow an empty (or all-conditions-stripped) filter through — each carries the code comment "An
empty filter (or one that resolves to zero conditions) must be allowed" immediately before
building the query with whatever where(...) conditions did end up present, none required:
await repo.deleteMany(); // deletes every row in the table — no error, no confirmation
await repo.updateMany({ active: false }); // sets every row's `active` to false
Both run without throwing. If your application wants to guard against an accidental full-table
wipe from a bug (e.g. a filter that ends up undefined due to a typo upstream), you need to
enforce that yourself at the call site — @sqb/connect will not do it for you.
Options types
Every method's options object extends Repository.CommandOptions
(connection, prettyPrint, comment, optimizerHint), adding a projection and/or
filter/params pair depending on the operation. findMany()'s options additionally add
sort/offset/limit/distinct/maxEagerFetch/maxSubQueries/onTransformRow — see
Repository.FindManyOptions for the full reference, and the
table on the CommandOptions page for how every
other Repository.*Options interface (CreateOptions, CountOptions, ExistsOptions,
FindOptions, FindOneOptions, DeleteOptions, DeleteManyOptions, UpdateOptions,
UpdateOnlyOptions, UpdateManyOptions) is built from it — none of those smaller interfaces adds
enough beyond CommandOptions + projection/filter/params to warrant its own page.
See Repositories → Filters and
Repositories → Projection for the filter and
projection syntax shared across all of them.