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
Comprehensive error handling with retryable error detection and detailed error information for robust application development.
Base error class for all Neo4j-specific errors with detailed error codes and messages.
class Neo4jError extends Error {
/** Neo4j error code (e.g., "Neo.ClientError.Statement.SyntaxError") */
code: string;
/**
* Create a new Neo4j error
* @param message - Error message describing the issue
* @param code - Optional Neo4j error code
*/
constructor(message: string, code?: string);
}
/**
* Create a new Neo4j error
* @param message - Error message
* @param code - Optional error code
* @returns Neo4jError instance
*/
function newError(message: string, code?: string): Neo4jError;Usage Examples:
import { driver, auth, Neo4jError, newError } from "neo4j-driver";
const session = neo4jDriver.session();
try {
// This will cause a syntax error
await session.run("INVALID CYPHER QUERY");
} catch (error) {
if (error instanceof Neo4jError) {
console.log(`Neo4j Error: ${error.message}`);
console.log(`Error Code: ${error.code}`);
// Handle specific error types
switch (error.code) {
case "Neo.ClientError.Statement.SyntaxError":
console.log("Cypher syntax error - check your query");
break;
case "Neo.ClientError.Security.Unauthorized":
console.log("Authentication failed - check credentials");
break;
case "Neo.TransientError.Network.DatabaseUnavailable":
console.log("Database unavailable - retry later");
break;
default:
console.log(`Unhandled error type: ${error.code}`);
}
} else {
console.log(`Other error: ${error.message}`);
}
} finally {
await session.close();
}
// Creating custom Neo4j errors
function validateInput(data: any) {
if (!data.name) {
throw newError("Name is required", "App.Validation.MissingName");
}
if (data.age < 0) {
throw newError("Age cannot be negative", "App.Validation.InvalidAge");
}
}Utility function to determine if an error should be retried.
/**
* Check if an error is retryable (transient error that may succeed on retry)
* @param error - Error to check for retryability
* @returns true if the error is retryable, false otherwise
*/
function isRetryableError(error: any): boolean;Usage Examples:
import { isRetryableError } from "neo4j-driver";
async function executeWithRetry<T>(operation: () => Promise<T>, maxRetries: number = 3): Promise<T> {
let lastError: any;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (!isRetryableError(error)) {
console.log(`Non-retryable error: ${error.message}`);
throw error;
}
if (attempt === maxRetries) {
console.log(`Max retries (${maxRetries}) reached`);
throw error;
}
const delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Usage with session operations
const session = neo4jDriver.session();
try {
const result = await executeWithRetry(async () => {
return await session.run(`
MATCH (p:Person {name: $name})
SET p.lastAccessed = datetime()
RETURN p
`, { name: "Alice" });
});
console.log("Operation succeeded after retries");
} catch (error) {
console.error("Operation failed after all retries:", error.message);
} finally {
await session.close();
}
// Check specific errors
try {
await session.run("MATCH (n) RETURN n");
} catch (error) {
if (isRetryableError(error)) {
console.log("This error can be retried:", error.message);
// Implement retry logic
} else {
console.log("This error should not be retried:", error.message);
// Handle as permanent failure
}
}Predefined constants for common Neo4j error codes.
declare const error: {
/** Service unavailable error code */
SERVICE_UNAVAILABLE: "Neo.TransientError.General.DatabaseUnavailable";
/** Session expired error code */
SESSION_EXPIRED: "Neo.TransientError.Transaction.Terminated";
/** Protocol error code */
PROTOCOL_ERROR: "Neo.ClientError.Request.Invalid";
};Usage Examples:
import { error } from "neo4j-driver";
try {
const result = await session.run("MATCH (n) RETURN count(n)");
} catch (err) {
if (err instanceof Neo4jError) {
switch (err.code) {
case error.SERVICE_UNAVAILABLE:
console.log("Database is unavailable - implement retry logic");
// Retry with exponential backoff
break;
case error.SESSION_EXPIRED:
console.log("Session expired - create new session");
// Close current session and create new one
break;
case error.PROTOCOL_ERROR:
console.log("Protocol error - check client/server compatibility");
// Log for debugging, don't retry
break;
case "Neo.ClientError.Statement.SyntaxError":
console.log("Cypher syntax error - fix the query");
break;
case "Neo.ClientError.Schema.ConstraintValidationFailed":
console.log("Constraint violation - handle business logic error");
break;
default:
console.log(`Unhandled error: ${err.code} - ${err.message}`);
}
}
}
// Using error constants in conditions
function shouldRetryError(error: any): boolean {
if (!error.code) return false;
const retryableCodes = [
error.SERVICE_UNAVAILABLE,
error.SESSION_EXPIRED,
"Neo.TransientError.Network.ConnectionTimeout",
"Neo.TransientError.Transaction.DeadlockDetected"
];
return retryableCodes.includes(error.code);
}Different types of errors require different handling strategies.
Transient Errors (Retryable):
// Network and availability errors
const transientErrors = [
"Neo.TransientError.General.DatabaseUnavailable",
"Neo.TransientError.Network.ConnectionTimeout",
"Neo.TransientError.Network.DatabaseUnavailable",
"Neo.TransientError.Transaction.DeadlockDetected",
"Neo.TransientError.Transaction.LockClientStopped"
];
async function handleTransientError<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (!isRetryableError(error) || attempt === maxRetries) {
throw error;
}
const delay = baseDelay * Math.pow(2, attempt - 1);
console.log(`Transient error, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error("Should not reach here");
}Client Errors (Non-Retryable):
// Syntax, constraint, and security errors
const clientErrors = [
"Neo.ClientError.Statement.SyntaxError",
"Neo.ClientError.Statement.SemanticError",
"Neo.ClientError.Schema.ConstraintValidationFailed",
"Neo.ClientError.Security.Unauthorized",
"Neo.ClientError.Security.Forbidden"
];
function handleClientError(error: Neo4jError): void {
switch (error.code) {
case "Neo.ClientError.Statement.SyntaxError":
console.error("Cypher syntax error:", error.message);
// Log query for debugging, don't retry
break;
case "Neo.ClientError.Schema.ConstraintValidationFailed":
console.error("Constraint violation:", error.message);
// Handle as business logic error
throw new Error(`Data validation failed: ${error.message}`);
case "Neo.ClientError.Security.Unauthorized":
console.error("Authentication failed:", error.message);
// Redirect to login or refresh token
throw new Error("Please log in again");
case "Neo.ClientError.Security.Forbidden":
console.error("Access denied:", error.message);
// Handle as authorization error
throw new Error("Insufficient permissions");
default:
console.error("Client error:", error.code, error.message);
throw error;
}
}Database Errors:
// Database-level errors
const databaseErrors = [
"Neo.DatabaseError.General.UnknownError",
"Neo.DatabaseError.Schema.IndexCreationFailed",
"Neo.DatabaseError.Transaction.TransactionStartFailed"
];
function handleDatabaseError(error: Neo4jError): void {
console.error("Database error:", error.code, error.message);
// Log for monitoring/alerting
logErrorForMonitoring(error);
// Database errors are usually not retryable by the client
throw new Error(`Database error: ${error.message}`);
}class Neo4jService {
private driver: Driver;
constructor(uri: string, auth: AuthToken) {
this.driver = driver(uri, auth);
}
async executeQuery<T>(
query: string,
parameters: Parameters = {},
transformer: (result: Result) => T
): Promise<T> {
const session = this.driver.session();
try {
const result = await this.withRetry(async () => {
return await session.run(query, parameters);
});
return transformer(result);
} catch (error) {
this.handleError(error, query, parameters);
throw error;
} finally {
await session.close();
}
}
private async withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
let lastError: any;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
if (!isRetryableError(error) || attempt === maxRetries) {
throw error;
}
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
console.log(`Retry attempt ${attempt} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
private handleError(error: any, query: string, parameters: Parameters): void {
if (!(error instanceof Neo4jError)) {
console.error("Non-Neo4j error:", error.message);
return;
}
// Log error details
console.error("Neo4j Error Details:", {
code: error.code,
message: error.message,
query: query.substring(0, 100) + "...",
parameterKeys: Object.keys(parameters)
});
// Categorize and handle error
if (error.code.startsWith("Neo.TransientError")) {
console.log("Transient error occurred - retry logic applied");
} else if (error.code.startsWith("Neo.ClientError")) {
this.handleClientError(error);
} else if (error.code.startsWith("Neo.DatabaseError")) {
this.handleDatabaseError(error);
}
}
private handleClientError(error: Neo4jError): void {
// Client error handling logic
if (error.code.includes("Security")) {
// Handle security errors
console.error("Security error - check authentication");
} else if (error.code.includes("Statement")) {
// Handle query errors
console.error("Query error - check Cypher syntax");
}
}
private handleDatabaseError(error: Neo4jError): void {
// Database error handling logic
console.error("Database error - may need administrator attention");
// Could send alert to monitoring system
this.sendAlert("Database Error", error);
}
private sendAlert(type: string, error: Neo4jError): void {
// Implementation for alerting system
console.log(`ALERT: ${type} - ${error.code}: ${error.message}`);
}
async close(): Promise<void> {
await this.driver.close();
}
}
// Usage
const neo4jService = new Neo4jService(
"neo4j://localhost:7687",
auth.basic("neo4j", "password")
);
try {
const people = await neo4jService.executeQuery(
"MATCH (p:Person) WHERE p.age > $minAge RETURN p",
{ minAge: 25 },
(result) => result.records.map(record => record.get("p").properties)
);
console.log(`Found ${people.length} people`);
} catch (error) {
console.error("Failed to fetch people:", error.message);
} finally {
await neo4jService.close();
}