Use interactive transactions with $transaction callback for atomic operations and automatic rollback. Use when operations must succeed or fail together.
Use `$transaction` callback for atomic multi-step operations that must succeed or fail together. Triggers when you need automatic rollback for complex business logic like financial transfers, inventory management, or dependent record creation.
/plugin marketplace add djankies/claude-configs/plugin install prisma-6@claude-configsThis skill is limited to using the following tools:
Use the $transaction callback API for operations that must succeed or fail atomically. Interactive transactions provide automatic rollback on errors and allow complex multi-step logic.
Use interactive transactions when:
Do NOT use for:
await prisma.$transaction(async (tx) => {
const result1 = await tx.model1.create({ data: { ... } });
const result2 = await tx.model2.update({
where: { id: result1.relatedId },
data: { ... }
});
return { result1, result2 };
});
All operations use the tx client. If any operation throws, the entire transaction rolls back automatically.
async function transferMoney(fromId: string, toId: string, amount: number) {
return await prisma.$transaction(async (tx) => {
const fromAccount = await tx.account.findUnique({
where: { id: fromId }
});
if (!fromAccount || fromAccount.balance < amount) {
throw new Error('Insufficient funds');
}
const updatedFrom = await tx.account.update({
where: { id: fromId },
data: { balance: { decrement: amount } }
});
const updatedTo = await tx.account.update({
where: { id: toId },
data: { balance: { increment: amount } }
});
const transfer = await tx.transfer.create({
data: {
fromAccountId: fromId,
toAccountId: toId,
amount
}
});
return { updatedFrom, updatedTo, transfer };
});
}
If any step fails, all changes roll back. Both accounts and the transfer record are consistent.
async function reserveInventory(orderId: string, items: Array<{ productId: string; quantity: number }>) {
return await prisma.$transaction(async (tx) => {
const order = await tx.order.update({
where: { id: orderId },
data: { status: 'PROCESSING' }
});
for (const item of items) {
const product = await tx.product.findUnique({
where: { id: item.productId }
});
if (!product || product.stock < item.quantity) {
throw new Error(`Insufficient stock for product ${item.productId}`);
}
await tx.product.update({
where: { id: item.productId },
data: { stock: { decrement: item.quantity } }
});
await tx.orderItem.create({
data: {
orderId,
productId: item.productId,
quantity: item.quantity,
price: product.price
}
});
}
return await tx.order.update({
where: { id: orderId },
data: { status: 'RESERVED' }
});
});
}
If stock is insufficient for any item, the entire reservation rolls back. No partial inventory deductions occur.
async function createUserWithProfile(userData: UserData, profileData: ProfileData) {
return await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: {
email: userData.email,
name: userData.name
}
});
const profile = await tx.profile.create({
data: {
userId: user.id,
bio: profileData.bio,
avatar: profileData.avatar
}
});
await tx.notification.create({
data: {
userId: user.id,
message: 'Welcome to our platform!'
}
});
return { user, profile };
});
}
User, profile, and notification are created atomically. If profile creation fails, the user is not created.
try {
const result = await prisma.$transaction(async (tx) => {
const step1 = await tx.model.create({ data: { ... } });
if (someCondition) {
throw new Error('Business rule violation');
}
const step2 = await tx.model.update({ ... });
return { step1, step2 };
});
} catch (error) {
console.error('Transaction failed, all changes rolled back:', error);
}
Any thrown error triggers automatic rollback. No manual cleanup needed.
await prisma.$transaction(async (tx) => {
}, {
timeout: 10000
});
Default timeout is 5 seconds. Increase for long-running transactions.
await prisma.$transaction(async (tx) => {
}, {
isolationLevel: 'Serializable'
});
Available levels: ReadUncommitted, ReadCommitted, RepeatableRead, Serializable. Default is database-specific.
Conditional Rollback:
await prisma.$transaction(async (tx) => {
const record = await tx.model.create({ data: { ... } });
if (!isValid(record)) {
throw new Error('Validation failed');
}
return record;
});
Dependent Operations:
await prisma.$transaction(async (tx) => {
const parent = await tx.parent.create({ data: { ... } });
const child = await tx.child.create({
data: { parentId: parent.id, ... }
});
return { parent, child };
});
Batch with Validation:
await prisma.$transaction(async (tx) => {
const records = await Promise.all(
items.map(item => tx.model.create({ data: item }))
);
if (records.length !== items.length) {
throw new Error('Not all records created');
}
return records;
});
Mixing transaction and non-transaction calls:
await prisma.$transaction(async (tx) => {
await tx.model.create({ data: { ... } });
await prisma.model.create({ data: { ... } });
});
Use tx for ALL operations inside the transaction.
Long-running operations:
await prisma.$transaction(async (tx) => {
await tx.model.create({ data: { ... } });
await fetch('https://api.example.com');
await tx.model.update({ ... });
});
Keep external API calls outside transactions. Transactions hold database locks.
Catching errors inside transaction:
await prisma.$transaction(async (tx) => {
try {
await tx.model.create({ data: { ... } });
} catch (error) {
}
await tx.model.update({ ... });
});
Let errors propagate to trigger rollback. Handle errors outside the transaction.
async (tx) => { ... }After implementing interactive transactions:
Interactive transactions ensure data consistency through automatic rollback and atomic execution.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.