TransactionalConnection
TransactionalConnection
The TransactionalConnection is a wrapper around the TypeORM Connection
object which works in conjunction
with the Transaction decorator to implement per-request transactions. All services which access the
database should use this class rather than the raw TypeORM connection, to ensure that db changes can be
easily wrapped in transactions when required.
The service layer does not need to know about the scope of a transaction, as this is covered at the
API by the use of the Transaction
decorator.
class TransactionalConnection {
constructor(dataSource: DataSource, transactionWrapper: TransactionWrapper)
rawConnection: DataSource
getRepository(target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
getRepository(ctx: RequestContext | undefined, target: ObjectType<Entity> | EntitySchema<Entity> | string, options?: {
replicationMode?: ReplicationMode;
}) => Repository<Entity>;
getRepository(ctxOrTarget: RequestContext | ObjectType<Entity> | EntitySchema<Entity> | string | undefined, maybeTarget?: ObjectType<Entity> | EntitySchema<Entity> | string, options?: {
replicationMode?: ReplicationMode;
}) => Repository<Entity>;
withTransaction(work: (ctx: RequestContext) => Promise<T>) => Promise<T>;
withTransaction(ctx: RequestContext, work: (ctx: RequestContext) => Promise<T>) => Promise<T>;
withTransaction(ctxOrWork: RequestContext | ((ctx: RequestContext) => Promise<T>), maybeWork?: (ctx: RequestContext) => Promise<T>) => Promise<T>;
startTransaction(ctx: RequestContext, isolationLevel?: TransactionIsolationLevel) => ;
commitOpenTransaction(ctx: RequestContext) => ;
rollBackTransaction(ctx: RequestContext) => ;
getEntityOrThrow(ctx: RequestContext, entityType: Type<T>, id: ID, options: GetEntityOrThrowOptions<T> = {}) => Promise<T>;
findOneInChannel(ctx: RequestContext, entity: Type<T>, id: ID, channelId: ID, options: FindOneOptions<T> = {}) => ;
findByIdsInChannel(ctx: RequestContext, entity: Type<T>, ids: ID[], channelId: ID, options: FindManyOptions<T>) => ;
}
constructor
(dataSource: DataSource, transactionWrapper: TransactionWrapper) => TransactionalConnection
rawConnection
DataSource
The plain TypeORM Connection object. Should be used carefully as any operations performed with this connection will not be performed within any outer transactions.
getRepository
(target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>
Returns a TypeORM repository. Note that when no RequestContext is supplied, the repository will not be aware of any existing transaction. Therefore, calling this method without supplying a RequestContext is discouraged without a deliberate reason.
getRepository
(ctx: RequestContext | undefined, target: ObjectType<Entity> | EntitySchema<Entity> | string, options?: {
replicationMode?: ReplicationMode;
}) => Repository<Entity>
Returns a TypeORM repository which is bound to any existing transactions. It is recommended to always pass the RequestContext argument when possible, otherwise the queries will be executed outside of any ongoing transactions which have been started by the Transaction decorator.
The options
parameter allows specifying additional configurations, such as the replicationMode
,
which determines whether the repository should interact with the master or replica database.
getRepository
(ctxOrTarget: RequestContext | ObjectType<Entity> | EntitySchema<Entity> | string | undefined, maybeTarget?: ObjectType<Entity> | EntitySchema<Entity> | string, options?: {
replicationMode?: ReplicationMode;
}) => Repository<Entity>
Returns a TypeORM repository. Depending on the parameters passed, it will either be transaction-aware
or not. If RequestContext
is provided, the repository is bound to any ongoing transactions. The
options
parameter allows further customization, such as selecting the replication mode (e.g., 'master').
withTransaction
Allows database operations to be wrapped in a transaction, ensuring that in the event of an error being thrown at any point, the entire transaction will be rolled back and no changes will be saved.
In the context of API requests, you should instead use the Transaction decorator on your resolver or controller method.
On the other hand, for code that does not run in the context of a GraphQL/REST request, this method should be used to protect against non-atomic changes to the data which could leave your data in an inconsistent state.
Such situations include function processed by the JobQueue or stand-alone scripts which make use of Vendure internal services.
If there is already a RequestContext object available, you should pass it in as the first argument in order to create transactional context as the copy. If not, omit the first argument and an empty RequestContext object will be created, which is then used to propagate the transaction to all inner method calls.
Example
private async transferCredit(outerCtx: RequestContext, fromId: ID, toId: ID, amount: number) {
await this.connection.withTransaction(outerCtx, async ctx => {
// Note you must not use `outerCtx` here, instead use `ctx`. Otherwise, this query
// will be executed outside of transaction
await this.giftCardService.updateCustomerCredit(ctx, fromId, -amount);
await this.connection.getRepository(ctx, GiftCard).update(fromId, { transferred: true })
// If some intermediate logic here throws an Error,
// then all DB transactions will be rolled back and neither Customer's
// credit balance will have changed.
await this.giftCardService.updateCustomerCredit(ctx, toId, amount);
})
}
withTransaction
(ctx: RequestContext, work: (ctx: RequestContext) => Promise<T>) => Promise<T>
withTransaction
(ctxOrWork: RequestContext | ((ctx: RequestContext) => Promise<T>), maybeWork?: (ctx: RequestContext) => Promise<T>) => Promise<T>
startTransaction
(ctx: RequestContext, isolationLevel?: TransactionIsolationLevel) =>
Manually start a transaction if one is not already in progress. This method should be used in
conjunction with the 'manual'
mode of the Transaction decorator.
commitOpenTransaction
(ctx: RequestContext) =>
Manually commits any open transaction. Should be very rarely needed, since the Transaction decorator and the internal TransactionInterceptor take care of this automatically. Use-cases include situations in which the worker thread needs to access changes made in the current transaction, or when using the Transaction decorator in manual mode.
rollBackTransaction
(ctx: RequestContext) =>
Manually rolls back any open transaction. Should be very rarely needed, since the Transaction decorator and the internal TransactionInterceptor take care of this automatically. Use-cases include when using the Transaction decorator in manual mode.
getEntityOrThrow
(ctx: RequestContext, entityType: Type<T>, id: ID, options: GetEntityOrThrowOptions<T> = {}) => Promise<T>
Finds an entity of the given type by ID, or throws an EntityNotFoundError
if none
is found.
findOneInChannel
(ctx: RequestContext, entity: Type<T>, id: ID, channelId: ID, options: FindOneOptions<T> = {}) =>
Like the TypeOrm Repository.findOne()
method, but limits the results to
the given Channel.
findByIdsInChannel
(ctx: RequestContext, entity: Type<T>, ids: ID[], channelId: ID, options: FindManyOptions<T>) =>
Like the TypeOrm Repository.findByIds()
method, but limits the results to
the given Channel.