UNPKG

qmemory

Version:

A comprehensive production-ready Node.js utility library with MongoDB document operations, user ownership enforcement, Express.js HTTP utilities, environment-aware logging, and in-memory storage. Features 96%+ test coverage with comprehensive error handli

158 lines (145 loc) 8.32 kB
/** * Database Utility Functions * MongoDB and Mongoose helper functions * * This module provides essential database connectivity and validation utilities * that ensure robust database operations throughout the application. These functions * implement defensive programming principles by validating database state before * operations and providing consistent error handling patterns. * * Key design principles: * - Fail fast: Check database connectivity before attempting operations * - Consistent error responses: Standardize HTTP responses for database issues * - Separation of concerns: Keep database validation separate from business logic * - Graceful degradation: Provide meaningful errors when database is unavailable * * These utilities are particularly important in cloud environments where database * connectivity can be intermittent, and in development environments where the * database might not always be running. */ // Mongoose provides the connection state monitoring we need for database validation const mongoose = require('mongoose'); // access connection state const { sendServiceUnavailable, sendInternalServerError, sendConflict } = require('./http-utils'); // HTTP response helpers /** * Database availability validation utility * * This function checks the MongoDB connection state before allowing operations * that require database access. It provides graceful degradation by responding * with appropriate HTTP errors when the database is unavailable. * * Connection state validation: Uses Mongoose connection readyState to determine * if the database is ready for operations. State 1 indicates a successful * connection, while other states represent disconnected, connecting, or error states. * * Design rationale: Prevents database operations from failing with unclear errors * by checking connectivity upfront. This approach provides better user experience * and clearer error messages when database issues occur. * * Error handling strategy: Sends appropriate HTTP status codes (503 for service * unavailable, 500 for unexpected errors) to inform clients about the specific * nature of the database issue. * * Alternative approaches considered: * - Ping the database: More thorough but adds latency to every request * - Middleware approach: Would require Express integration, reducing flexibility * - Connection pooling checks: More complex and may not be necessary for simple cases * * Trade-offs: * - Performance: Fast check using existing connection state vs. actual database ping * - Accuracy: Connection state might be stale, but actual operations will catch real issues * - Simplicity: Simple boolean return allows easy integration into existing code * * @param {Object} res - Express response object for sending database error responses * @returns {boolean} Boolean indicating whether database is available for operations * * Side effects: Sends HTTP error responses when database is unavailable * Usage pattern: Controllers should check this before database operations */ function ensureMongoDB(res) { console.log('ensureMongoDB is running'); // entry log for tracing calls // Log current connection state for debugging and monitoring purposes // readyState values: 0=disconnected, 1=connected, 2=connecting, 3=disconnecting // This logging is crucial for diagnosing connectivity issues in cloud environments console.log(`Checking database availability - connection state: ${mongoose.connection.readyState}`); // records state for debugging try { // Check if MongoDB connection is in ready state (1 = connected) // We use readyState !== 1 instead of === 0 to catch all non-ready states // This is a fast, non-blocking check that uses existing connection information if (mongoose.connection.readyState !== 1) { // quick readyState check over ping prioritizes speed but may miss stale connections sendServiceUnavailable(res, 'Database functionality unavailable'); // send 503 when DB not ready console.log('Database check failed - connection not ready'); // log failure path console.log('ensureMongoDB is returning false'); // trace unsuccessful return return false; // stop DB operations when connection not ready } console.log('Database check passed - connection ready'); // log success path console.log('ensureMongoDB is returning true'); // trace successful return return true; // allow calling code to use DB } catch (error) { console.error('Database availability check error:', error); // log unexpected failure sendInternalServerError(res, 'Error checking database connection'); // respond with 500 on check error console.log('ensureMongoDB is returning false'); // trace error return return false; // fail safe when check throws } } /** * Document uniqueness validation utility * * This function checks if a document matching the given query already exists * in the database, preventing duplicate records and enforcing business rules. * It's commonly used before creating new documents or updating existing ones * where uniqueness constraints need to be maintained. * * Design rationale: * - Prevents race conditions by checking immediately before operations * - Provides consistent HTTP 409 Conflict responses for duplicate attempts * - Centralizes uniqueness logic to avoid duplication across controllers * - Returns boolean for easy integration into conditional logic * * Performance considerations: * - Uses findOne() which stops at first match, more efficient than find() * - Query should include indexed fields for optimal performance * - Consider database-level unique constraints for critical uniqueness requirements * * Race condition handling: * - This check + insert pattern has an inherent race condition window * - For critical uniqueness, consider using database unique indexes or upsert operations * - Current approach balances simplicity with adequate protection for most use cases * * @param {Object} model - Mongoose model to query against * @param {Object} query - MongoDB query object to check for existing documents * @param {Object} res - Express response object for sending duplicate error responses * @param {string} duplicateMsg - Custom message to send when duplicate is found * @returns {Promise<boolean>} Promise resolving to true if unique, false if duplicate exists */ async function ensureUnique(model, query, res, duplicateMsg) { console.log('ensureUnique is running'); // entry log for uniqueness checks // Log the query for debugging and monitoring duplicate attempt patterns // JSON.stringify ensures complex queries are readable in logs for troubleshooting console.log(`ensureUnique is checking query: ${JSON.stringify(query)}`); // log query for audit try { // Use findOne for efficiency - we only need to know if ANY matching document exists // findOne() stops at first match, making it faster than find() which returns all matches // This is critical for performance when checking uniqueness in large collections const existing = await model.findOne(query); // fast check may race; index enforcement would be safer if (existing) { // Send 409 Conflict status code - the standard HTTP response for duplicate resource attempts // Use centralized HTTP utility for consistent conflict responses across all endpoints sendConflict(res, duplicateMsg); // respond 409 for duplicates console.log('ensureUnique found duplicate'); // log detection console.log('ensureUnique is returning false'); // trace duplicate return return false; // caller should abort create/update } console.log('ensureUnique passed - no duplicates'); // log success state console.log('ensureUnique is returning true'); // trace success return return true; // safe to continue with unique data } catch (error) { console.error('ensureUnique error', error); // log DB error throw error; // let caller decide handling } } // Export functions using object shorthand for clean module interface // This pattern provides a consistent export structure across all utility modules module.exports = { ensureMongoDB, // validates DB availability ensureUnique // checks for duplicate docs };