The official Neo4j driver for JavaScript applications, enabling connection to and interaction with Neo4j graph databases.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Transaction handling including explicit transactions and managed transactions with automatic retry logic for robust database operations.
Manual transaction management with full control over commit and rollback operations.
interface Transaction {
/**
* Run a Cypher query within the transaction
* @param query - The Cypher query string
* @param parameters - Query parameters as key-value pairs
* @returns Promise resolving to query result
*/
run(query: string, parameters?: Parameters): Promise<Result>;
/** Commit the transaction and make changes permanent */
commit(): Promise<void>;
/** Rollback the transaction and discard changes */
rollback(): Promise<void>;
/** Check if the transaction is still open and active */
isOpen(): boolean;
}Usage Examples:
const session = neo4jDriver.session();
// Basic transaction usage
const tx = await session.beginTransaction();
try {
const result = await tx.run(
"CREATE (p:Person {name: $name}) RETURN p",
{ name: "Alice" }
);
await tx.commit();
console.log("Transaction committed");
} catch (error) {
await tx.rollback();
console.error("Transaction rolled back:", error);
} finally {
await session.close();
}
// Complex multi-step transaction
const complexTx = await session.beginTransaction({
timeout: 60000, // 1 minute timeout
metadata: { operation: "user_registration" }
});
try {
// Step 1: Create user
const userResult = await complexTx.run(`
CREATE (u:User {id: randomUUID(), email: $email, createdAt: datetime()})
RETURN u.id AS userId
`, { email: "user@example.com" });
const userId = userResult.records[0].get("userId");
// Step 2: Create user profile
await complexTx.run(`
MATCH (u:User {id: $userId})
CREATE (u)-[:HAS_PROFILE]->(p:Profile {
firstName: $firstName,
lastName: $lastName,
updatedAt: datetime()
})
`, {
userId,
firstName: "John",
lastName: "Doe"
});
// Step 3: Add to default groups
await complexTx.run(`
MATCH (u:User {id: $userId}), (g:Group {name: 'users'})
CREATE (u)-[:MEMBER_OF]->(g)
`, { userId });
await complexTx.commit();
console.log(`User ${userId} registered successfully`);
} catch (error) {
await complexTx.rollback();
console.error("User registration failed:", error);
} finally {
await session.close();
}Managed transactions with automatic retry logic and simplified error handling.
interface ManagedTransaction {
/**
* Run a Cypher query within the managed transaction
* @param query - The Cypher query string
* @param parameters - Query parameters as key-value pairs
* @returns Promise resolving to query result
*/
run(query: string, parameters?: Parameters): Promise<Result>;
}
interface Session {
/**
* Execute a read transaction with automatic retry logic
* @param work - Function containing read operations
* @param config - Optional transaction configuration
* @returns Promise resolving to the work function result
*/
executeRead<T>(
work: (tx: ManagedTransaction) => Promise<T>,
config?: TransactionConfig
): Promise<T>;
/**
* Execute a write transaction with automatic retry logic
* @param work - Function containing write operations
* @param config - Optional transaction configuration
* @returns Promise resolving to the work function result
*/
executeWrite<T>(
work: (tx: ManagedTransaction) => Promise<T>,
config?: TransactionConfig
): Promise<T>;
}Usage Examples:
const session = neo4jDriver.session();
try {
// Read transaction - automatically retried on failure
const users = await session.executeRead(async tx => {
const result = await tx.run(`
MATCH (u:User)-[:HAS_PROFILE]->(p:Profile)
WHERE u.createdAt > datetime({year: 2023})
RETURN u.email, p.firstName, p.lastName
ORDER BY u.createdAt DESC
LIMIT 100
`);
return result.records.map(record => ({
email: record.get("u.email"),
firstName: record.get("p.firstName"),
lastName: record.get("p.lastName")
}));
});
console.log(`Found ${users.length} recent users`);
// Write transaction with retry logic
const orderId = await session.executeWrite(async tx => {
// Create order
const orderResult = await tx.run(`
CREATE (o:Order {
id: randomUUID(),
status: 'pending',
createdAt: datetime(),
total: $total
})
RETURN o.id AS orderId
`, { total: 99.99 });
const id = orderResult.records[0].get("orderId");
// Add order items
await tx.run(`
MATCH (o:Order {id: $orderId})
UNWIND $items AS item
CREATE (o)-[:CONTAINS]->(oi:OrderItem {
productId: item.productId,
quantity: item.quantity,
price: item.price
})
`, {
orderId: id,
items: [
{ productId: "prod-123", quantity: 2, price: 49.99 },
{ productId: "prod-456", quantity: 1, price: 0.01 } // Tax adjustment
]
});
return id;
}, {
timeout: 30000,
metadata: { operation: "create_order", version: "v2" }
});
console.log(`Created order: ${orderId}`);
} finally {
await session.close();
}Configuration options for controlling transaction behavior.
interface TransactionConfig {
/** Transaction timeout in milliseconds */
timeout?: number;
/**
* Transaction metadata for monitoring and debugging
* Useful for tracking operations in Neo4j logs
*/
metadata?: Record<string, any>;
}Usage Examples:
// Long-running transaction with custom timeout
await session.executeWrite(async tx => {
// Bulk data import operation
const result = await tx.run(`
LOAD CSV WITH HEADERS FROM $csvUrl AS row
CREATE (p:Product {
id: row.id,
name: row.name,
price: toFloat(row.price),
category: row.category,
importedAt: datetime()
})
`, { csvUrl: "file:///import/products.csv" });
return result.summary.counters.nodesCreated;
}, {
timeout: 300000, // 5 minutes for large import
metadata: {
operation: "bulk_import",
source: "products.csv",
batchId: "batch-001"
}
});
// Transaction with detailed metadata
await session.executeWrite(async tx => {
await tx.run(`
MATCH (u:User {id: $userId})
SET u.lastLogin = datetime(),
u.loginCount = coalesce(u.loginCount, 0) + 1
`, { userId: "user-123" });
}, {
metadata: {
operation: "user_login",
userId: "user-123",
timestamp: new Date().toISOString(),
source: "web_app"
}
});Retry Logic: Managed transactions automatically retry on transient errors like connection issues or deadlocks. The driver uses exponential backoff and respects cluster topology changes.
Read vs Write Transactions:
executeRead for queries that only read dataexecuteWrite for queries that modify dataTransaction Size:
Error Handling:
Performance Tips:
// Good: Focused transaction
await session.executeWrite(async tx => {
await tx.run(`
MATCH (u:User {id: $userId})
SET u.status = $status, u.updatedAt = datetime()
`, { userId, status: "active" });
});
// Good: Bulk operation with UNWIND
await session.executeWrite(async tx => {
await tx.run(`
UNWIND $users AS user
MERGE (u:User {id: user.id})
SET u.email = user.email, u.updatedAt = datetime()
`, { users: userBatch });
});
// Avoid: Transaction too large
await session.executeWrite(async tx => {
// Too many operations in single transaction
for (const user of allUsers) { // Could be thousands
await tx.run("CREATE (u:User $props)", { props: user });
}
});