Skip to content

DatabaseConfig Developer Documentation

Overview

The DatabaseConfig class manages database configuration settings with validation and defaults. It provides a standardized configuration object for Database instances, ensuring all settings are validated and properly formatted before use.

Key Responsibilities:

  • Configuration validation and normalization
  • Default value management
  • Type checking and constraint enforcement
  • Configuration cloning and serialization

Design Principles:

  • Fail-fast validation in constructor
  • Configuration intended to be treated as immutable after creation
  • Clear error messages for invalid settings
  • Sensible defaults for all properties

Constants

DatabaseConfig internally defines shared constants for log handling so the configuration stays in sync with JDbLogger behaviour. These are implementation details used for validation and are not exposed as public static properties or part of the public API:

  • LOG_LEVELS: ordered array of accepted level names (['DEBUG','INFO','WARN','ERROR']) used during validation.
  • DEFAULT_LOG_LEVEL: default value ('INFO') applied when no explicit level is supplied.

Configuration Properties

Core Properties

Property Type Default Description
rootFolderId String Drive root folder Root Drive folder for database files
masterIndexKey String 'GASDB_MASTER_INDEX' ScriptProperties key for master index

Optional Properties

Property Type Default Description
autoCreateCollections Boolean true Auto-create collections when accessed
lockTimeout Number 30000 Legacy compatibility alias for collectionLockLeaseMs; when provided on its own it seeds both timeout settings
collectionLockLeaseMs Number 30000 Collection lock lease duration in milliseconds; must be at least as long as coordinationTimeoutMs
coordinationTimeoutMs Number 30000 Maximum duration allowed for a coordinated collection operation
retryAttempts Number 3 Lock acquisition retry attempts
retryDelayMs Number 1000 Delay between lock retries
lockRetryBackoffBase Number 2 Exponential backoff base for lock retries
cacheEnabled Boolean true Enable file caching
logLevel String DEFAULT_LOG_LEVEL ('INFO') Log level constrained to the values declared in LOG_LEVELS
fileRetryAttempts Number 3 File operation retry attempts
fileRetryDelayMs Number 1000 Delay between file retries
fileRetryBackoffBase Number 2 Exponential backoff base for file retries
queryEngineMaxNestedDepth Number 10 Maximum allowed query nesting depth
queryEngineSupportedOperators String[] ['$eq','$gt','$lt','$and','$or'] Operators permitted by the QueryEngine
queryEngineLogicalOperators String[] ['$and','$or'] Logical operators recognised by the QueryEngine
backupOnInitialise Boolean false If true, Database.initialise() will create/find the Drive index file and back up the MasterIndex immediately. If false, the backup index is created lazily on first write (e.g. creating/dropping a collection) or when loadIndex() is called.
stripDisallowedCollectionNameCharacters Boolean false When enabled, invalid characters are stripped from collection names before validation so integrations that cannot guarantee clean inputs can still rely on strict reserved-name and empty-name checks.

Constructor

class DatabaseConfig {
  constructor(config = {}) {
    // ...
  }
}

Parameters:

  • config (Object): Configuration options object

Behaviour:

  • Delegates to grouped helpers (_initialiseGeneralDefaults, _initialiseRetryConfig, _initialiseQueryEngineConfig, _initialiseBooleanFlags) so each concern applies defaults consistently.
  • Uses nullish coalescing (??) to normalise nullish overrides while keeping explicit falsy values intact.
  • Sets default values for all properties
  • Validates all configuration parameters
  • Throws errors immediately for invalid settings
  • Automatically determines root folder if not provided

Example:

const config = new DatabaseConfig({
  rootFolderId: 'your-folder-id',
  autoCreateCollections: false,
  logLevel: 'DEBUG'
});

API Reference

Public Methods

clone()

Creates a deep copy of the configuration.

  • Returns: DatabaseConfig - New configuration instance
  • Use Cases:
  • Creating modified configurations
  • Immutable configuration management
  • Testing with variations

toJSON()

Converts configuration to a serialisable plain object with a __type tag.

  • Returns: Object - Plain object suitable for JSON serialisation
  • Use Cases:
  • Serialisation
  • Debugging and logging
  • Configuration comparison

fromJSON(obj)

Creates a DatabaseConfig from an object produced by toJSON().

  • Parameters: obj (Object) - A deserialised config object containing __type: 'DatabaseConfig'
  • Returns: DatabaseConfig
  • Throws: InvalidArgumentError if the object is not a valid DatabaseConfig serialisation

Private Methods

_initialiseGeneralDefaults(config)

Applies root folder, split coordination timing defaults, log level (via DEFAULT_LOG_LEVEL), and master index defaults.

_initialiseRetryConfig(config)

Normalises retry-related options (lock and file retries) with grouped defaults.

_initialiseQueryEngineConfig(config)

Clones provided operator arrays when supplied and records raw values for validation while restoring defaults when omitted.

_initialiseBooleanFlags(config)

Initialises boolean feature toggles such as autoCreateCollections and backupOnInitialise.

_getDefaultRootFolder()

Determines the default root folder ID.

  • Returns: String - Drive root folder ID
  • Throws: Error if unable to access Drive
  • Behaviour: Uses DriveApp.getRootFolder().getId()

_validateConfig()

Validates all configuration properties according to rules.

  • Throws: Error for any validation failure
  • Validation Order:
  • Type checking
  • Range validation
  • Format validation
  • Constraint checking

_validateQueryOperators()

Ensures logical operators are a subset of supported operators and raises INVALID_ARGUMENT when alignment fails.

_validateOperatorArray(configKey, operators, rawValue, wasProvided)

Validates optional operator arrays against type constraints and preserves the original error context.

Validation Rules

Property Validation

lockTimeout / collectionLockLeaseMs / coordinationTimeoutMs:

  • Each value must be a number of at least 500ms
  • lockTimeout is retained as a legacy alias; new code should prefer the split properties
  • collectionLockLeaseMs must be greater than or equal to coordinationTimeoutMs
  • CollectionCoordinator may renew a lease shortly before final metadata persistence, but the configured lease should still comfortably cover the main write callback
  • Recommended range: 5000-60000ms unless you have a measured need for longer operations
  • Zero is invalid because the minimum is enforced during validation

retryAttempts / retryDelayMs / lockRetryBackoffBase:

  • retryAttempts must be a positive integer
  • retryDelayMs must be a non-negative number
  • lockRetryBackoffBase must be a positive number

logLevel:

  • Must be one of the entries in LOG_LEVELS (currently 'DEBUG', 'INFO', 'WARN', 'ERROR')
  • Case-sensitive validation
  • Defaults to DEFAULT_LOG_LEVEL to remain consistent with JDbLogger
  • Affects logging verbosity across the system

rootFolderId:

  • Must be a valid string if provided
  • Auto-detected if not specified
  • Should correspond to accessible Drive folder
  • Default root folder lookups are cached after first access to avoid repeated Drive API calls

Boolean Properties:

  • autoCreateCollections: Must be true or false
  • cacheEnabled: Must be true or false
  • Type coercion not performed
  • stripDisallowedCollectionNameCharacters: Must be true or false; default false enforces strict rejection of invalid names, while true enables sanitisation.

masterIndexKey:

  • Must be a non-empty string
  • Used as ScriptProperties key
  • Should be unique per database instance

File retry settings:

  • fileRetryAttempts must be a positive integer
  • fileRetryDelayMs must be a non-negative number
  • fileRetryBackoffBase must be a positive number

QueryEngine settings:

  • queryEngineMaxNestedDepth must be an integer greater than or equal to zero
  • queryEngineSupportedOperators must be a non-empty array of non-empty strings
  • queryEngineLogicalOperators must be a non-empty array of non-empty strings, each present in queryEngineSupportedOperators

Error Scenarios

Common validation errors and their meanings:

Error Message Cause Solution
"Lock timeout must be a number" Invalid timeout value Use a number of at least 500ms
"Log level must be one of: DEBUG, INFO, WARN, ERROR" Invalid log level Use exact string match
"Auto create collections must be a boolean" Non-boolean value Use true or false explicitly
"Failed to get default root folder" Drive access issue Check permissions and authentication
"Collection sanitisation flag must be a boolean" Non-boolean value for stripDisallowedCollectionNameCharacters Use true or false explicitly

Usage Examples

Basic Configuration

// Minimal configuration (uses defaults)
const config = new DatabaseConfig();

// Custom root folder
const config = new DatabaseConfig({
  rootFolderId: '1ABC123xyz789'
});

// Production-ready configuration
const config = new DatabaseConfig({
  rootFolderId: 'prod-folder-id',
  autoCreateCollections: false,
  collectionLockLeaseMs: 12000,
  coordinationTimeoutMs: 10000,
  logLevel: 'WARN',
  // Avoid Drive file churn on start; backups occur lazily or explicitly
  backupOnInitialise: false
});

Development vs Production

// Development configuration
const devConfig = new DatabaseConfig({
  autoCreateCollections: true,
  logLevel: 'DEBUG',
  cacheEnabled: true,
  // Enable eager backup if you want an index snapshot each initialise
  backupOnInitialise: true,
  // Looser query depth for exploratory work
  queryEngineMaxNestedDepth: 12
});

// Production configuration
const prodConfig = new DatabaseConfig({
  rootFolderId: 'production-folder-id',
  autoCreateCollections: false,
  collectionLockLeaseMs: 8000,
  coordinationTimeoutMs: 5000,
  logLevel: 'ERROR',
  cacheEnabled: true,
  backupOnInitialise: false
});

Configuration Cloning

const baseConfig = new DatabaseConfig({
  rootFolderId: 'base-folder',
  logLevel: 'INFO'
});

// Create variation for testing
const testConfig = baseConfig.clone();
// Note: Properties are read-only after creation
// Clone and create new instance for modifications

const modifiedConfig = new DatabaseConfig({
  ...baseConfig.toJSON(),
  logLevel: 'DEBUG'
});

Configuration Serialization

const config = new DatabaseConfig({
  autoCreateCollections: false,
  collectionLockLeaseMs: 20000,
  coordinationTimeoutMs: 15000
});

// Serialize for storage or logging
const configData = config.toJSON();
console.log('Config:', JSON.stringify(configData, null, 2));

// Recreate from serialised data
const restoredConfig = DatabaseConfig.fromJSON(configData);

Integration with Database

The DatabaseConfig class integrates seamlessly with the Database class. For Apps Script consumers, prefer the public API factories:

// First-time setup
const db1 = JsonDbApp.createAndInitialiseDatabase(
  new DatabaseConfig({
    masterIndexKey: 'myMasterIndex'
  })
);

// Load existing database
const db2 = JsonDbApp.loadDatabase({
  masterIndexKey: 'myMasterIndex',
  logLevel: 'DEBUG',
  // Only back up MasterIndex to Drive during initialise when explicitly enabled
  backupOnInitialise: false
});

// Configuration is validated when constructing DatabaseConfig/Database
try {
  JsonDbApp.loadDatabase({ coordinationTimeoutMs: -1 }); // Will throw
} catch (error) {
  console.error('Invalid configuration:', error.message);
}

Component-Level Configuration

While DatabaseConfig handles database-wide settings, individual components may have their own configuration options:

QueryEngine Configuration

The QueryEngine uses defaults sourced from DatabaseConfig and is instantiated through DocumentOperations. You can still override defaults by passing a custom config directly to QueryEngine when needed.

// QueryEngine has its own configuration
const queryEngine = new QueryEngine({
  maxNestedDepth: 15 // Override default of 10
});

// DatabaseConfig supplies defaults for collections
const dbConfig = new DatabaseConfig({
  logLevel: 'DEBUG',
  collectionLockLeaseMs: 25000,
  coordinationTimeoutMs: 20000,
  queryEngineMaxNestedDepth: 12,
  queryEngineSupportedOperators: ['$eq', '$gt', '$lt', '$and', '$or']
});

const db = new Database(dbConfig);
// Collections use the QueryEngine internally with DatabaseConfig defaults

QueryEngine Options:

  • maxNestedDepth (Number, default: 10): Maximum allowed query nesting depth for security
  • supportedOperators (String[], default: ['$eq', '$gt', '$lt', '$and', '$or']): Operators permitted by the engine
  • logicalOperators (String[], default: ['$and', '$or']): Logical operators permitted by the engine

FileOperations Configuration

File operations use retry settings from DatabaseConfig when the Database constructs FileOperations:

  • fileRetryAttempts: Number of retry attempts for Drive operations
  • fileRetryDelayMs: Delay between retries
  • fileRetryBackoffBase: Exponential backoff base for retries

Security Note: QueryEngine always validates all queries for structure and supported operators to prevent malicious queries, regardless of configuration.

Index Backup Strategy

backupOnInitialise controls whether Database.initialise() performs Drive index creation and backup:

  • false (default): Initialise reads from MasterIndex only; no Drive writes are performed. The Drive-based index is created lazily on first write operation (e.g., createCollection, dropCollection) or when calling database.loadIndex().
  • true: Initialise will create/find the index file in Drive and back up the current MasterIndex immediately. This is useful when you want a snapshot on every start at the cost of additional Drive operations.

Independent of this flag, explicit calls to database.backupIndexToDrive() will always perform a backup if an index file exists.

Best Practices

  1. Validate early: Create DatabaseConfig instances explicitly to catch errors
  2. Use appropriate timeouts: Size collectionLockLeaseMs to cover the whole write, and keep coordinationTimeoutMs within that lease
  3. Environment-specific configs: Create different configurations for dev/test/prod
  4. Immutable configurations: Don't modify config properties after creation
  5. Proper folder permissions: Ensure root folder is accessible to the script
  6. Sensible logging levels: Use DEBUG for development, INFO/WARN for production
  7. Document custom settings: Comment why specific values were chosen
  8. Test configuration validation: Include config validation in your tests
  9. Security-first design: All components prioritise security over optional performance optimisations
  10. Component separation: Each component manages its own configuration appropriate to its responsibilities