Connection Management
arango‑typed provides a Mongoose‑like connect() and a connection cache that reuses arangojs Database instances for identical connection parameters. It can also auto‑create the target database if it doesn't exist.
What is Connection Management?
Connection management in arango-typed handles establishing, caching, and reusing database connections to ArangoDB. It provides a simple, Mongoose-like API while optimizing performance through connection reuse and automatic database creation.
Why Use arango-typed Connection Management?
- Performance: Connection caching eliminates overhead from repeated connections
- Simplicity: Mongoose-like API that's familiar and easy to use
- Auto-creation: Automatically creates databases if they don't exist (configurable)
- Helper Functions: Convenient access to database, graph manager, vector search, and more
- Error Handling: Descriptive error messages with automatic retry on database creation
- Cluster Support: Built-in support for ArangoDB clusters with multiple coordinators
Connection Formats
arango-typed supports multiple connection formats for flexibility:
1. URI Format (Mongoose-like)
The simplest format, similar to Mongoose's connection string:
import { connect } from 'arango-typed';
// Format: http://host:port/database
await connect('http://localhost:8529/myapp', {
username: 'root',
password: ''
});
// With authentication
await connect('http://localhost:8529/production', {
username: 'admin',
password: 'secret123'
});
2. Simplified Object Format
Clean, readable object format with flat properties:
await connect({
url: 'http://localhost:8529',
database: 'myapp',
username: 'root',
password: ''
});
// All options
await connect({
url: 'http://localhost:8529',
database: 'myapp',
username: 'root',
password: '',
autoCreateDatabase: true,
arangoVersion: 30800
});
3. Full Object Format (Backward Compatible)
Full format with nested auth object for compatibility:
await connect({
url: 'http://localhost:8529',
databaseName: 'myapp', // Can also use 'database'
auth: {
username: 'root',
password: ''
},
autoCreateDatabase: true
});
Auto-Create Database
By default, arango-typed automatically creates the database if it doesn't exist. This is perfect for development but should be disabled in production for safety.
How It Works
- Attempts to connect to the specified database
- If database not found, connects to
_systemdatabase - Creates the target database using
createDatabase() - Reconnects to the newly created database
- Caches the connection for future use
Enable Auto-Create (Default)
// Auto-create enabled by default
await connect({
url: 'http://localhost:8529',
database: 'appdb',
username: 'root',
password: ''
// autoCreateDatabase: true (default)
});
Disable Auto-Create (Production)
// Disable for production safety
await connect({
url: 'http://localhost:8529',
database: 'production',
username: 'admin',
password: process.env.ARANGO_PASSWORD,
autoCreateDatabase: false // Explicitly disable
});
Environment-Based Auto-Create
await connect({
url: process.env.ARANGO_URL,
database: process.env.ARANGO_DB,
username: process.env.ARANGO_USER,
password: process.env.ARANGO_PASS,
autoCreateDatabase: process.env.NODE_ENV !== 'production' // Only in dev/test
});
Connection Caching
arango-typed automatically caches connections to improve performance. Connections are cached by a key composed of url|database|username.
How Caching Works
- First Connection: Creates new Database instance and caches it
- Subsequent Calls: Checks cache for matching connection parameters
- Health Check: Performs lightweight
version()check to verify connection is still valid - Reuse: Returns cached connection if valid, otherwise creates new one
Example: Connection Reuse
// First call - creates and caches connection
await connect('http://localhost:8529/appdb', { username: 'root', password: '' });
// Second call - reuses cached connection (fast!)
await connect('http://localhost:8529/appdb', { username: 'root', password: '' });
// Different database - creates new connection
await connect('http://localhost:8529/otherdb', { username: 'root', password: '' });
// Different user - creates new connection
await connect('http://localhost:8529/appdb', { username: 'admin', password: '' });
Cache Key Generation
The cache key is generated from: url|database|username
- Same URL, database, and username = same cache entry
- Different password doesn't affect cache (authentication happens per request)
- Cluster URLs use the first coordinator URL for cache key
Helper Functions
After connecting, use helper functions to access various services bound to the default connection:
getDatabase()
Get the arangojs Database instance for direct AQL queries:
import { connect, getDatabase } from 'arango-typed';
await connect('http://localhost:8529/myapp', { username: 'root', password: '' });
const db = getDatabase();
// Use arangojs directly
const cursor = await db.query('FOR doc IN users RETURN doc');
const users = await cursor.all();
getVectorSearch()
Get VectorSearch instance for similarity search:
import { getVectorSearch } from 'arango-typed';
const vectorSearch = getVectorSearch();
const results = await vectorSearch.similaritySearch('documents', queryVector, { limit: 10 });
getGraphManager()
Get GraphManager instance for graph operations:
import { getGraphManager } from 'arango-typed';
const graphManager = getGraphManager();
const graph = await graphManager.createGraph('social', {
vertices: ['users', 'posts'],
edges: ['follows', 'likes']
});
getTransactionManager()
Get TransactionManager instance for ACID transactions:
import { getTransactionManager } from 'arango-typed';
const txManager = getTransactionManager();
const result = await txManager.execute(async (trx) => {
// Transaction operations
await trx.collection('users').save({ name: 'John' });
await trx.collection('posts').save({ title: 'Hello' });
return { success: true };
});
getSearchManager()
Get SearchManager instance for full-text search:
import { getSearchManager } from 'arango-typed';
const searchManager = getSearchManager();
const results = await searchManager.search('documents', 'search query', {
limit: 10
});
getGeoQuery()
Get GeoQuery instance for geospatial queries:
import { getGeoQuery } from 'arango-typed';
const geoQuery = getGeoQuery();
const nearby = await geoQuery.near('locations', {
latitude: 40.7128,
longitude: -74.0060,
radius: 1000 // meters
});
getBulkOperations()
Get BulkOperations instance for batch operations:
import { getBulkOperations } from 'arango-typed';
const bulkOps = getBulkOperations();
await bulkOps.import('users', [
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' }
]);
Connection Options
Complete list of connection options with detailed explanations:
url (string | string[])
ArangoDB server URL(s). For clusters, provide an array of coordinator URLs.
// Single server
url: 'http://localhost:8529'
// HTTPS
url: 'https://arangodb.example.com:8529'
// Cluster (array of coordinators)
url: [
'http://coordinator1:8529',
'http://coordinator2:8529',
'http://coordinator3:8529'
]
database / databaseName (string)
Database name. Both database and databaseName are supported for compatibility.
database: 'myapp'
// or
databaseName: 'myapp'
username / password (string)
Authentication credentials. Can be provided as flat properties or nested in auth object.
// Flat format
username: 'root',
password: 'secret'
// Nested format
auth: {
username: 'root',
password: 'secret'
}
agent (any)
HTTP agent for custom connection settings (keep-alive, proxies, TLS).
import { Agent } from 'https';
const agent = new Agent({
keepAlive: true,
maxSockets: 50
});
await connect({
url: 'https://arangodb.example.com:8529',
database: 'myapp',
username: 'admin',
password: 'secret',
agent: agent
});
arangoVersion (number)
Optional ArangoDB version hint for arangojs compatibility (e.g., 30800 for 3.8.0).
arangoVersion: 30800 // 3.8.0
arangoVersion: 31100 // 3.11.0
autoCreateDatabase (boolean)
Whether to automatically create the database if it doesn't exist. Default: true.
autoCreateDatabase: true // Enable (default)
autoCreateDatabase: false // Disable (production)
Cluster Connections
For ArangoDB clusters, provide multiple coordinator URLs. arango-typed will automatically handle failover and load balancing.
Basic Cluster Connection
await connect({
url: [
'http://coordinator1:8529',
'http://coordinator2:8529',
'http://coordinator3:8529'
],
database: 'myapp',
username: 'root',
password: 'secret'
});
Cluster with HTTPS
await connect({
url: [
'https://coordinator1.example.com:8529',
'https://coordinator2.example.com:8529',
'https://coordinator3.example.com:8529'
],
database: 'production',
username: 'admin',
password: process.env.ARANGO_PASSWORD
});
Environment-Based Configuration
Use environment variables for different environments (development, staging, production):
Basic Environment Setup
import { connect } from 'arango-typed';
import dotenv from 'dotenv';
dotenv.config();
await connect({
url: process.env.ARANGO_URL || 'http://localhost:8529',
database: process.env.ARANGO_DB || 'myapp',
username: process.env.ARANGO_USER || 'root',
password: process.env.ARANGO_PASS || '',
autoCreateDatabase: process.env.NODE_ENV !== 'production'
});
Production Configuration
// .env.production
// ARANGO_URL=https://arangodb.example.com:8529
// ARANGO_DB=production
// ARANGO_USER=admin
// ARANGO_PASS=secure_password_here
await connect({
url: process.env.ARANGO_URL!,
database: process.env.ARANGO_DB!,
username: process.env.ARANGO_USER!,
password: process.env.ARANGO_PASS!,
autoCreateDatabase: false // Never auto-create in production
});
Configuration Module Pattern
// config/db.ts
import { connect, getDatabase } from 'arango-typed';
export async function connectDB() {
try {
await connect({
url: process.env.ARANGO_URL || 'http://localhost:8529',
database: process.env.ARANGO_DB || 'myapp',
username: process.env.ARANGO_USER || 'root',
password: process.env.ARANGO_PASS || '',
autoCreateDatabase: process.env.NODE_ENV !== 'production'
});
const db = getDatabase();
console.log('✅ ArangoDB connected');
return db;
} catch (error) {
console.error('❌ ArangoDB connection failed:', error);
throw error;
}
}
// app.ts
import { connectDB } from './config/db';
await connectDB();
Error Handling
Connection errors throw ConnectionError with descriptive messages. Always wrap connect() in try-catch for proper error handling.
Basic Error Handling
import { connect, ConnectionError } from 'arango-typed';
try {
await connect({
url: 'http://localhost:8529',
database: 'myapp',
username: 'root',
password: ''
});
console.log('Connected successfully');
} catch (error) {
if (error instanceof ConnectionError) {
console.error('Connection error:', error.message);
} else {
console.error('Unexpected error:', error);
}
process.exit(1);
}
Error with Auto-Create
If auto-create fails, the error message includes both the original error and the creation error:
try {
await connect({
url: 'http://localhost:8529',
database: 'myapp',
username: 'root',
password: '',
autoCreateDatabase: true
});
} catch (error) {
// Error message includes: "Failed to connect to ArangoDB: [original error] (auto-create failed: [creation error])"
console.error(error.message);
}
Retry Logic
async function connectWithRetry(maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
await connect({
url: process.env.ARANGO_URL!,
database: process.env.ARANGO_DB!,
username: process.env.ARANGO_USER!,
password: process.env.ARANGO_PASS!
});
return;
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`Retry ${i + 1}/${maxRetries}...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
Connection Lifecycle
Understanding how connections are managed throughout your application:
1. First Connection
- Validates connection parameters
- Creates new arangojs Database instance
- Attempts to connect to specified database
- If database not found and auto-create enabled, creates database
- Performs health check (
version()) - Caches connection for reuse
2. Subsequent Connections
- Checks cache for matching connection parameters
- If found, performs lightweight health check
- Returns cached connection if valid
- Creates new connection if cache miss or invalid
3. Health Checks
On connection reuse, a lightweight version() check verifies the connection is still valid. If the check fails, the cache entry is removed and a new connection is created.
4. Error Recovery
If a connection fails, the cache entry is automatically cleared, allowing retry logic to create a fresh connection.
Complete Application Setup
Full example of connection setup in a production application:
// config/db.ts
import { connect, getDatabase, ConnectionError } from 'arango-typed';
import dotenv from 'dotenv';
dotenv.config();
let isConnected = false;
export async function initializeDatabase() {
if (isConnected) {
return getDatabase();
}
try {
const db = await connect({
url: process.env.ARANGO_URL || 'http://localhost:8529',
database: process.env.ARANGO_DB || 'myapp',
username: process.env.ARANGO_USER || 'root',
password: process.env.ARANGO_PASS || '',
autoCreateDatabase: process.env.NODE_ENV !== 'production',
arangoVersion: process.env.ARANGO_VERSION ? parseInt(process.env.ARANGO_VERSION) : undefined
});
isConnected = true;
console.log(`✅ Connected to ArangoDB: ${process.env.ARANGO_DB}`);
// Verify connection
const version = await db.version();
console.log(`📊 ArangoDB version: ${version.version}`);
return db;
} catch (error) {
if (error instanceof ConnectionError) {
console.error('❌ Connection error:', error.message);
} else {
console.error('❌ Unexpected error:', error);
}
throw error;
}
}
// Graceful shutdown
export async function closeDatabase() {
// arangojs handles connection cleanup automatically
// No explicit close needed, but you can clear cache if needed
isConnected = false;
}
// app.ts
import { initializeDatabase } from './config/db';
async function startApp() {
try {
await initializeDatabase();
// Start your application
} catch (error) {
console.error('Failed to start application:', error);
process.exit(1);
}
}
startApp();
Advanced Topics
Custom HTTP Agent
Use custom HTTP agent for proxies, TLS settings, or connection pooling:
import { Agent } from 'https';
import { connect } from 'arango-typed';
const agent = new Agent({
keepAlive: true,
maxSockets: 50,
rejectUnauthorized: false // For self-signed certificates
});
await connect({
url: 'https://arangodb.example.com:8529',
database: 'myapp',
username: 'admin',
password: 'secret',
agent: agent
});
Connection State Checking
import { getConnection, getDatabase } from 'arango-typed';
// Check if connected
const connection = getConnection();
if (connection.isConnected()) {
const db = getDatabase();
// Use database
} else {
// Reconnect
await connect({ /* ... */ });
}
Multiple Database Connections
While arango-typed uses a default connection, you can manage multiple connections:
import { Connection } from 'arango-typed';
// Create separate connection instances
const conn1 = new Connection({
url: 'http://localhost:8529',
databaseName: 'db1',
auth: { username: 'root', password: '' }
});
const conn2 = new Connection({
url: 'http://localhost:8529',
databaseName: 'db2',
auth: { username: 'root', password: '' }
});
await conn1.connect();
await conn2.connect();
const db1 = conn1.getDatabase();
const db2 = conn2.getDatabase();
Best Practices
- Connect Once: Connect once at application startup, reuse throughout. Don't call
connect()in every request. - Environment Variables: Never hardcode credentials. Use environment variables or secrets management (AWS Secrets Manager, HashiCorp Vault, etc.).
- Auto-create in Development Only: Enable
autoCreateDatabasein development/test, disable in production. - Error Handling: Always wrap
connect()in try-catch and handle errors gracefully. - Health Checks: Implement health check endpoints that verify database connectivity.
- Connection Pooling: arangojs handles connection pooling automatically; no manual management needed.
- Graceful Shutdown: Although not required, you can implement cleanup logic for graceful shutdown.
- Logging: Log connection events (success, failure, retries) for debugging and monitoring.
- TypeScript: Use TypeScript for type safety and better IDE support.
Troubleshooting
Connection Refused
- Verify ArangoDB server is running
- Check URL and port are correct
- Verify firewall settings
Authentication Failed
- Verify username and password are correct
- Check user has access to the database
- Verify authentication method (basic auth vs. JWT)
Database Not Found
- Enable
autoCreateDatabasefor development - Manually create database in ArangoDB web interface
- Verify database name spelling
Slow Connections
- Check network latency
- Verify connection caching is working (should reuse connections)
- Consider using connection pooling with custom HTTP agent