Database Developer Documentation¶
- Database Developer Documentation
- Overview
- Architecture \& MasterIndex Integration
- Database Initialization and Recovery Workflow
- Constructor
- API Reference
- Core Methods
createDatabase()initialise()recoverDatabase(backupFileId)getCollection(name)createCollection(name)listCollections()dropCollection(name)loadIndex()backupIndexToDrive()- Private Methods
_findExistingIndexFile()_createIndexFile()ensureIndexFile()_resolveCollection(resolvedName, originalName)_createCollectionObject(name, driveFileId)_normaliseIndexData(rawData)_assertIndexObject(indexCandidate)_ensureCollectionsMap(indexData)_ensureLastUpdated(indexData)_validateCollectionName(name)
- Usage Examples
- Integration with MasterIndex
- Error Types
- Best Practices
Overview¶
The Database class is the main entry point for GAS DB operations, providing high-level database management and collection coordination. It serves as an orchestrator that delegates collection metadata management to MasterIndex while maintaining Drive-based index files for backup and migration purposes.
Key Responsibilities:
- Database creation, initialization, and recovery
- Collection creation, access, and deletion
- MasterIndex as single source of truth for metadata
- Drive index file backup operations
- High-level database operations and error handling
Dependencies:
MasterIndex: Single source of truth for collection metadataFileService: Drive API operations and cachingDatabaseConfig: Configuration managementJDbLogger: Logging and monitoring
Architecture & MasterIndex Integration¶
Refactored Design (Single Source of Truth)¶
Following the initialization refactoring, the Database class now enforces MasterIndex as the single source of truth:
Database (Orchestrator)
├── MasterIndex (Single source of truth - ScriptProperties)
├── Drive Index File (Backup only)
├── FileService (Drive operations)
└── Collections Map (In-memory cache)
Data Sources¶
Single Source of Truth - MasterIndex:
- Fast access via ScriptProperties
- Authoritative for all collection metadata
- Virtual locking and conflict detection
- Cross-instance coordination
- Used for all normal operations
Backup Only - Drive Index File:
- Backup and durability
- Migration and recovery scenarios
- Explicit synchronization via
backupIndexToDrive()
Collection Access Protocol¶
UPDATED: The getCollection() method validates the caller input, logs any sanitisation, and delegates to the shared _resolveCollection(resolvedName, originalName) helper for consistent caching and error messaging. (Note: The collection() alias was removed in v0.0.5.)
- Public method sanitises and validate the name up front, logging when characters were stripped while keeping the unsanitised input for messaging
_resolveCollection()checks the in-memory collections cache using the sanitised key- If not cached, the helper queries MasterIndex (single source of truth)
- When the collection is still missing,
_resolveCollection()auto-creates if permitted, otherwise it raises an error that includes the caller’s original name
Important: _resolveCollection() ensures the caller sees the original name in error messages when auto-create is disabled, even if sanitisation adjusted the lookup key. The helper also short-circuits when the configuration forbids auto-creation
You can tune the behaviour by enabling stripDisallowedCollectionNameCharacters on DatabaseConfig. When enabled, the sanitised name is the one persisted in caches and the MasterIndex, yet _resolveCollection() keeps the unsanitised input for messaging parity so developers can reconcile errors with their original call site.
Database Initialization and Recovery Workflow¶
NEW: The Database class now enforces a clear separation between creation, initialization, and recovery:
First-Time Setup¶
Using the public API (recommended for Apps Script library consumers):
Or using the class directly (within this project):
Normal Initialization¶
Using the public API:
Or directly:
Disaster Recovery¶
const db = new Database(config);
db.recoverDatabase(backupFileId); // Restores from backup to MasterIndex
db.initialise(); // Loads from restored MasterIndex
Benefits of New Workflow¶
- Single Source of Truth: MasterIndex is the authoritative source, eliminating race conditions
- Clear Separation: Distinct operations for creation, initialization, and recovery
- Fail-Fast: Explicit errors when MasterIndex is missing or corrupted
- Simplified Operations: No dual-source complexity in collection methods
Constructor¶
Parameters:
config(Object|DatabaseConfig): Database configuration object or DatabaseConfig instance
Behaviour:
- Validates and normalizes configuration via DatabaseConfig
- initialises logging, file services, and MasterIndex
- Creates in-memory collections map
- Does NOT automatically initialise - call
initialise()explicitly
Example:
const db = new Database({
rootFolderId: 'your-folder-id',
autoCreateCollections: true,
logLevel: 'DEBUG'
});
API Reference¶
Core Methods¶
createDatabase()¶
NEW: Creates a new database for first-time setup.
- Returns:
void - Throws:
Errorwhen MasterIndex already exists or creation fails - Use Cases:
- First-time database setup
- Clean database creation for new projects
Process:
- Check if MasterIndex already exists in ScriptProperties
- If exists, throw error directing user to recovery process
- Create fresh MasterIndex with empty collections
- Persist to ScriptProperties
initialise()¶
REFACTORED: initialises the database from MasterIndex only (single source of truth).
- Returns:
void - Throws:
Errorwhen MasterIndex is missing, corrupted, or initialization fails - Side Effects:
- Loads collections from MasterIndex into memory
- When
backupOnInitialiseistrue, ensures a Drive-based index file exists for backups - When
backupOnInitialiseistrue, backs up the MasterIndex to the Drive index file
Process:
- Verify MasterIndex exists and is valid
- Load existing collections from MasterIndex (single source of truth)
- When
backupOnInitialiseistrue, create or find the Drive-based index file used for backups - When
backupOnInitialiseistrueand collections exist, back up the MasterIndex to the Drive index file - Populate in-memory collections cache
Important: No longer falls back to Drive index file. If MasterIndex is missing or corrupted, use createDatabase() for fresh setup or recoverDatabase() for recovery.
recoverDatabase(backupFileId)¶
NEW: Recovers database from a backup index file.
- Parameters:
backupFileId(String): Drive file ID of backup index file- Returns:
Array<String>- Names of recovered collections - Throws:
Errorwhen recovery fails or backup file is invalid - Use Cases:
- Disaster recovery when MasterIndex is lost or corrupted
- Migration from old backup files
- Data restoration scenarios
Process:
- Load and validate backup index file structure
- Create fresh MasterIndex for recovery
- Restore collections from backup to MasterIndex
- Persist restored MasterIndex to ScriptProperties
getCollection(name)¶
UPDATED: Gets or creates a collection (MasterIndex only).
- Parameters:
name(String): Collection name- Returns:
Object- Collection object - Throws:
Errorfor invalid names or when collection doesn't exist and auto-create is disabled
Access Path:
- Public method sanitises and validates the name, logging adjustments
_resolveCollection()uses the sanitised name for cache and MasterIndex lookups- Cache hit returns immediately; otherwise MasterIndex metadata is used to build the collection
- Auto-create (if enabled) creates the collection before returning; when disabled the helper throws with the caller's original name
Important:
- No longer falls back to Drive index file
- When auto-create is disabled,
_resolveCollection()raises an error that includes the caller's original name - The
collection()alias was removed in v0.0.5 - usegetCollection()as the canonical method
createCollection(name)¶
Explicitly creates a new collection.
- Parameters:
name(String): Collection name- Returns:
Object- Created collection object - Throws:
Errorfor invalid names or if collection already exists
Process:
- Validate collection name
- Check for existing collection in MasterIndex and memory
- Create Drive file with initial collection data
- Add to MasterIndex (primary)
- Add to Drive index file (backup)
- Add to in-memory cache
listCollections()¶
UPDATED: Lists all collection names from MasterIndex only.
- Returns:
Array<String>- Collection names - Data Source: MasterIndex (single source of truth)
Important: No longer falls back to Drive index file.
dropCollection(name)¶
UPDATED: Deletes a collection and its data (MasterIndex only).
- Parameters:
name(String): Collection name to delete- Returns:
Boolean- Success status - Throws:
Errorfor invalid collection names
Process:
- Find collection in MasterIndex (single source of truth)
- Delete collection Drive file
- Remove from in-memory cache
- Remove from MasterIndex
- Remove from Drive index file (backup updates occur only when Drive backups are enabled)
Important: No longer falls back to Drive index file.
loadIndex()¶
Loads and validates Drive-based index file data.
- Returns:
Object- Index file data with structure validation - Throws:
ErrorHandler.ErrorTypes.INVALID_FILE_FORMATfor corrupted or structurally invalid payloads,ErrorHandler.ErrorTypes.FILE_IO_ERRORwhen Drive reads fail
Validation & Repair:
_normaliseIndexData()ensures the parsed value is an object, applying default envelopes and surfacingINVALID_FILE_FORMATerrors when validation fails_assertIndexObject()rejects malformed payloads (including top-level arrays) usingErrorHandler.ErrorTypes.INVALID_FILE_FORMAT_ensureCollectionsMap()guaranteescollectionsis a map, repairing missing entries and raisingINVALID_FILE_FORMATwhen an existing value has the wrong type_ensureLastUpdated()backfills timestamps when absent
These helpers are also used by index mutation utilities so that validation and repair logic remains centralised. Before reading the Drive index file, loadIndex() calls ensureIndexFile() so the file is only created or touched when backups are enabled (backupOnInitialise: true) or when an explicit index operation needs it.
Regression suites cover array payload rejection and timestamp preservation so that future changes retain these guarantees.
backupIndexToDrive()¶
Explicitly backs up MasterIndex data to Drive-based index file.
- Returns:
Boolean- Success status - Use Cases:
- Periodic backups
- Before major operations
- Data migration scenarios
Private Methods¶
_findExistingIndexFile()¶
Searches root folder for existing database index files.
- Returns:
String|null- File ID if found - Search Pattern: Files containing 'database_index' and ending with '.json'
_createIndexFile()¶
Creates a new Drive-based index file with initial structure.
- Side Effects: Sets
this.indexFileId - Initial Structure: Empty collections, timestamps, version
ensureIndexFile()¶
Lazily locates or creates the Drive-based index file. This helper is invoked when backupOnInitialise is true during initialise() and before any other Drive index operations so unnecessary Drive writes are avoided when backups are disabled.
_resolveCollection(resolvedName, originalName)¶
Centralises collection resolution for getCollection() after the public wrapper sanitises the input.
- Parameters:
resolvedName(String): Sanitised collection name used for cache and MasterIndex lookupsoriginalName(String): Caller-supplied name used for error messaging and auto-create delegations- Returns:
Object- Collection object from cache or newly constructed via MasterIndex metadata - Behaviour:
- Checks cache before contacting MasterIndex
- Rehydrates a collection using MasterIndex metadata when available
- Throws with the original caller input when auto-create is disabled and the collection is missing
- Creates and caches new collections (via
createCollection(originalName)) when auto-create is permitted
_buildCollectionMetadataPayload(name, fileId, documentCount = 0) ⭐ NEW in v0.0.5¶
Added in: DB3 refactoring
Centralized helper for constructing collection metadata payloads with consistent field structure.
- Parameters:
name(String): Collection namefileId(String): Drive file ID for the collectiondocumentCount(Number): Initial document count (default: 0)- Returns:
Object- Metadata payload with standardized fields - Structure:
- Usage: Used by
addCollectionToMasterIndex(),addCollectionToIndex(),_restoreCollectionFromBackup(), and collection management operations - Benefits: Single source of truth for metadata structure, guaranteed field alignment
_wrapMasterIndexError(operation, error, messagePrefix) ⭐ NEW in v0.0.5¶
Added in: DB2 refactoring
Wraps non-GASDB errors into MasterIndexError with consistent formatting.
- Parameters:
operation(String): Operation identifier for error contexterror(Error): Original error to wrapmessagePrefix(String): Prefix for error message- Returns:
Error- Original GASDB error or new MasterIndexError - Behaviour:
- Returns error unchanged if already a GASDB_ERROR
- Creates new MASTER_INDEX_ERROR for non-GASDB errors
- Formats message as:
${messagePrefix}: ${error.message} - Usage: Used by
createDatabase(),initialise(),recoverDatabase() - Benefits: Consistent error wrapping, preserves error types for tests
_createCollectionObject(name, driveFileId)¶
Creates a minimal collection object (placeholder for full Collection class).
- Parameters:
name(String): Collection namedriveFileId(String): Drive file ID- Returns:
Object- Collection object - Note: Currently returns minimal object; will integrate with full Collection class in Section 5
_normaliseIndexData(rawData)¶
Prepares parsed index content for validation and repair.
- Parameters:
rawData(any): Value returned byJSON.parse- Behaviour:
- Delegates to
_assertIndexObject()to reject anything other than a plain object whilst expecting callers to supply an object-shaped payload - Does not coerce primitives, arrays, or
nullinto objects; it simply verifies the input already meets the contract - Ensures the collections map and
lastUpdatedtimestamp exist by calling_ensureCollectionsMap()and_ensureLastUpdated() - Returns the original object reference so callers persist a repaired but familiar structure
_assertIndexObject(indexCandidate)¶
Guarantees the index payload has an object shape.
- Parameters:
indexCandidate(any): Value from_normaliseIndexData()- Throws:
ErrorHandler.ErrorTypes.INVALID_FILE_FORMATwhen the payload is not a plain object (rejects arrays, primitives,null, dates, etc.) - Behaviour:
- Rejects unsupported payloads early so Drive corruption surfaces immediately
- Returns the candidate unchanged when it is a plain object
_ensureCollectionsMap(indexData)¶
Ensures collections exists and has the expected type.
- Parameters:
indexData(Object): Index data returned by_assertIndexObject()- Behaviour:
- Repairs a missing
collectionsproperty by seeding an empty map - Throws
ErrorHandler.ErrorTypes.INVALID_FILE_FORMATwhen an existingcollectionsvalue is not an object map - Preserves valid maps so timestamp comparisons remain stable
_ensureLastUpdated(indexData)¶
Ensures the index metadata has a lastUpdated timestamp.
- Parameters:
indexData(Object): Index object returned by_assertIndexObject()- Behaviour:
- Adds a timestamp when missing or invalid
- Keeps existing timestamps intact so tests can assert stability
_validateCollectionName(name)¶
Validates collection name according to GAS DB rules.
- Parameters:
name(String): Name to validate- Throws:
Errorfor invalid names - Rules:
- Must be non-empty string
- No invalid filesystem characters (
[\/\\:*?"<>|]). WhenstripDisallowedCollectionNameCharactersis enabled on the configuration, the characters are removed before the checks below, and any modification is logged for diagnostics. - Not reserved names (index, master, system, admin). The reserved-name check is applied after sanitisation so
index/will still be rejected even though the slash is removed.
Usage Examples¶
Basic Database Setup¶
const config = new DatabaseConfig({
rootFolderId: 'your-drive-folder-id',
autoCreateCollections: true,
logLevel: 'INFO'
});
const db = new Database(config);
await db.initialise();
console.log('Available collections:', db.listCollections());
Collection Management¶
// Create collection explicitly
const users = db.createCollection('users');
// Get existing collection (or auto-create if enabled)
const posts = db.getCollection('posts');
// List all collections
const allCollections = db.listCollections();
console.log('Collections:', allCollections);
// Drop collection
const success = db.dropCollection('temp_collection');
Index File Operations¶
// Load index file data
try {
const indexData = db.loadIndex();
console.log('Index contains:', Object.keys(indexData.collections));
} catch (error) {
console.error('Index file corrupted:', error.message);
}
// Backup MasterIndex to Drive
const backupSuccess = db.backupIndexToDrive();
if (backupSuccess) {
console.log('Backup completed successfully');
}
Error Handling¶
try {
const collection = db.getCollection('invalid/name');
} catch (error) {
if (error.message.includes('invalid characters')) {
console.error('Collection name validation failed');
}
}
// Handle initialization errors
try {
db.initialise();
} catch (error) {
console.error('Database initialization failed:', error.message);
// Implement recovery logic
}
Integration with MasterIndex¶
Collection Lifecycle¶
// Collection Creation Flow
db.createCollection('users');
// 1. Validate name
// 2. Create Drive file
// 3. Add to MasterIndex (primary)
// 4. Add to Drive index (backup)
// 5. Cache in memory
Data Synchronization¶
The Database class maintains consistency between data sources:
- MasterIndex → Memory: On initialization and collection access
- Index File → MasterIndex: When collections found only in index file
- MasterIndex → Index File: Via explicit
backupIndexToDrive()
Backup Strategy¶
// Periodic backup pattern
setInterval(
() => {
if (db.backupIndexToDrive()) {
console.log('Periodic backup completed');
}
},
30 * 60 * 1000
); // Every 30 minutes
Error Types¶
- Invalid Configuration: Configuration validation failures
- Collection Name Validation: Invalid characters or reserved names
- Collection Already Exists: Duplicate collection creation
- Collection Not Found: Accessing non-existent collections
- Index File Corruption: JSON parsing or structure validation errors
- Drive API Errors: File operation failures
- Initialization Errors: Database setup failures
Best Practices¶
- Always initialise explicitly: Call
initialise()after constructor - Handle collection name validation: Use try-catch for collection operations
- Implement periodic backups: Use
backupIndexToDrive()regularly - Monitor index file health: Check for corruption and implement recovery
- Use proper error handling: Different error types require different recovery strategies
- Leverage auto-create judiciously: Enable for development, consider disabling for production
- Cache collection references: Store collection objects to avoid repeated lookups
- Clean up properly: Use
dropCollection()to remove unused collections