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.
Signature
class TransactionalConnection {
constructor(connection: Connection, transactionWrapper: TransactionWrapper)
rawConnection: Connection
getRepository(target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
getRepository(ctx: RequestContext | undefined, target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
getRepository(ctxOrTarget: RequestContext | ObjectType<Entity> | EntitySchema<Entity> | string | undefined, maybeTarget?: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>;
async withTransaction(work: (ctx: RequestContext) => Promise<T>) => Promise<T>;
async withTransaction(ctx: RequestContext, work: (ctx: RequestContext) => Promise<T>) => Promise<T>;
async withTransaction(ctxOrWork: RequestContext | ((ctx: RequestContext) => Promise<T>), maybeWork?: (ctx: RequestContext) => Promise<T>) => Promise<T>;
async startTransaction(ctx: RequestContext, isolationLevel?: TransactionIsolationLevel) => ;
async commitOpenTransaction(ctx: RequestContext) => ;
async rollBackTransaction(ctx: RequestContext) => ;
async 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>) => ;
}
Members
constructor
(connection: Connection, transactionWrapper: TransactionWrapper) => TransactionalConnection
rawConnection
Connection
getRepository
(target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>
getRepository
(ctx: RequestContext | undefined, target: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>
getRepository
(ctxOrTarget: RequestContext | ObjectType<Entity> | EntitySchema<Entity> | string | undefined, maybeTarget?: ObjectType<Entity> | EntitySchema<Entity> | string) => Repository<Entity>
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) =>
'manual'
mode of the Transaction decorator.
commitOpenTransaction
(ctx: RequestContext) =>
rollBackTransaction
(ctx: RequestContext) =>
getEntityOrThrow
(ctx: RequestContext, entityType: Type<T>, id: ID, options: GetEntityOrThrowOptions<T> = {}) => Promise<T>
EntityNotFoundError
if none
is found.
findOneInChannel
(ctx: RequestContext, entity: Type<T>, id: ID, channelId: ID, options: FindOneOptions<T> = {}) =>
Repository.findOne()
method, but limits the results to
the given Channel.
findByIdsInChannel
(ctx: RequestContext, entity: Type<T>, ids: ID[], channelId: ID, options: FindManyOptions<T>) =>
Repository.findByIds()
method, but limits the results to
the given Channel.