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
262 lines (232 loc) • 10.9 kB
JavaScript
/**
* QMemory Library Demo Application
* Demonstrates core functionality with a simple Express.js API
*
* This demo application showcases the practical usage of the qmemory library
* in a real Express.js server environment. It provides a complete REST API
* for user management operations using the library's utilities.
*
* Design rationale:
* - Demonstrates library integration patterns for real applications
* - Shows proper error handling using library HTTP utilities
* - Provides working examples of all major library features
* - Serves as both documentation and functional testing platform
*
* Architecture decisions:
* - Uses in-memory storage for simplicity and quick demonstration
* - Implements comprehensive logging for debugging and monitoring
* - Follows REST conventions for intuitive API design
* - Includes health checks for production readiness demonstration
*/
const express = require('express');
const {
MemStorage,
sendNotFound,
sendInternalServerError,
ensureMongoDB
} = require('./index');
const app = express();
const port = process.env.PORT || 5000;
// Local helper functions provide minimal implementations since the library
// no longer exports these utilities
function logInfo(...args) { console.log('[INFO]', ...args); } // unify info logging
function logError(...args) { console.error('[ERROR]', ...args); } // unify error logging
function sendSuccess(res, message, data) { // send standard 200 response
console.log(`sendSuccess is running with ${message}`); // trace call for debugging
try {
const payload = { message, timestamp: new Date().toISOString() };
if (data !== undefined) payload.data = data; // include optional data
res.status(200).json(payload);
console.log(`sendSuccess is returning ${JSON.stringify(payload)}`); // confirm output
} catch (error) {
console.error('sendSuccess failed', error); // log failure path
}
}
function sendBadRequest(res, message) { // send standard 400 response
console.log(`sendBadRequest is running with ${message}`); // trace call for debugging
try {
const payload = { message, timestamp: new Date().toISOString() };
res.status(400).json(payload);
console.log('sendBadRequest has run resulting in a final value of 400'); // confirm completion
} catch (error) {
console.error('sendBadRequest failed', error); // log failure path
}
}
function sanitizeInput(str) { // remove spaces and html tags to mitigate xss
console.log(`sanitizeInput is running with ${str}`); // trace sanitizer use
try {
const value = typeof str === 'string' ? str.trim().replace(/<[^>]*>/g, '') : '';
console.log(`sanitizeInput is returning ${value}`); // confirm sanitized result
return value;
} catch (error) {
console.error('sanitizeInput failed', error); // log sanitizer errors
return '';
}
}
// Middleware
app.use(express.json()); // body parser for JSON payloads, ensures consistent req.body
app.use(express.static('public')); // serve static files for documentation and example assets
// Initialize storage
const storage = new MemStorage(); // in-memory store is used to keep the demo self contained
// Logging middleware
app.use((req, res, next) => {
const start = Date.now(); // capture start time to calculate response duration
res.on('finish', () => {
const duration = Date.now() - start; // measure request processing time
logInfo(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`); // log concise request details for monitoring
});
next();
});
// Health check endpoint
app.get('/health', async (req, res) => { // returns service status to support uptime monitoring
try {
const userCount = (await storage.getAllUsers()).length; // await to get accurate user total
const health = {
status: 'healthy', // simple status message used by monitoring tools
uptime: process.uptime(), // uptime info provides quick readiness metric
memory: process.memoryUsage(), // snapshot of memory usage for debugging
userCount, // confirm storage interaction
timestamp: new Date().toISOString()
};
sendSuccess(res, 'Service is healthy', health); // send standardized success
} catch (error) {
logError('Health check failed', error); // log for operator visibility
sendInternalServerError(res, 'Health check failed');
}
});
// Demo API endpoints
app.get('/', (req, res) => { // basic API index for manual exploration
res.json({
title: 'QMemory Library Demo',
description: 'Production-ready Node.js utility library demonstration',
endpoints: {
'GET /health': 'Health check and system status',
'GET /users': 'List all users',
'POST /users': 'Create new user (JSON: {username, displayName})', // updated to reflect display name usage
'GET /users/:id': 'Get user by ID',
'DELETE /users/:id': 'Delete user by ID',
'POST /users/clear': 'Clear all users (development only)'
},
examples: {
createUser: {
method: 'POST',
url: '/users',
body: { username: 'johndoe', displayName: 'John Doe' } // changed example payload field
}
}
});
});
// User management endpoints
app.get('/users', async (req, res) => { // list all stored users for testing purposes
try {
const users = await storage.getAllUsers(); // await promise to get user array reliably
sendSuccess(res, `Found ${users.length} users`, users);
} catch (error) {
logError('Failed to fetch users', error); // log ensures visibility in dev
sendInternalServerError(res, 'Failed to fetch users');
}
});
app.post('/users', async (req, res) => { // create a user for demo operations
try {
const { username, displayName } = req.body; // get required username and optional display name
const safeName = sanitizeInput(username); // sanitize inputs to prevent XSS and maintain consistent storage
const safeDisplay = sanitizeInput(displayName); // sanitize optional display name field
if (!safeName) { // ensure sanitized username exists
return sendBadRequest(res, 'Username is required and must be a string');
}
const user = await storage.createUser({ username: safeName, displayName: safeDisplay }); // pass only fields used by storage
logInfo(`Created user: ${safeName}`); // record creation event for auditing
sendSuccess(res, 'User created successfully', user);
} catch (error) {
if (error.message.includes('already exists')) {
sendBadRequest(res, error.message); // duplicate user results in 400
} else {
logError('Failed to create user', error); // other errors flagged as 500
sendInternalServerError(res, 'Failed to create user');
}
}
});
app.get('/users/:id', async (req, res) => { // fetch a single user by id
try {
const id = parseInt(req.params.id, 10); // parse as base-10 integer for clarity
if (!Number.isInteger(id) || !/^\d+$/.test(req.params.id)) { // validate numeric input strictly
return sendBadRequest(res, 'User ID must be numeric'); // reject non-numeric IDs with 400
}
const user = await storage.getUser(id); // await user fetch to ensure data
if (!user) { // handle unknown id
return sendNotFound(res, 'User not found');
}
sendSuccess(res, 'User found', user);
} catch (error) {
logError('Failed to fetch user', error); // log before sending generic error
sendInternalServerError(res, 'Failed to fetch user');
}
});
app.delete('/users/:id', async (req, res) => { // remove a user by id
try {
const id = parseInt(req.params.id, 10); // parse as base-10 integer for consistency
if (!Number.isInteger(id) || !/^\d+$/.test(req.params.id)) { // enforce numeric id format
return sendBadRequest(res, 'User ID must be numeric'); // reject invalid ids with 400
}
const deleted = await storage.deleteUser(id); // await deletion result
if (!deleted) { // handle missing user
return sendNotFound(res, 'User not found');
}
logInfo(`Deleted user with ID: ${id}`); // audit deletion event
sendSuccess(res, 'User deleted successfully');
} catch (error) {
logError('Failed to delete user', error); // preserve stack for debugging
sendInternalServerError(res, 'Failed to delete user');
}
});
app.post('/users/clear', async (req, res) => { // wipe storage when testing
if (process.env.NODE_ENV === 'production') {
return sendBadRequest(res, 'Clear operation not allowed in production'); // protect production data
}
try {
await storage.clear(); // await to ensure cleanup completes
logInfo('Cleared all users'); // log maintenance activity
sendSuccess(res, 'All users cleared successfully');
} catch (error) {
logError('Failed to clear users', error); // log for debugging
sendInternalServerError(res, 'Failed to clear users');
}
});
// Error handling middleware
app.use((error, req, res, next) => {
logError('Unhandled error:', error); // capture unexpected issues
if (res.headersSent) { // delegate to default handler if headers already sent
return next(error);
}
sendInternalServerError(res, 'An unexpected error occurred'); // hide error specifics from clients
});
// 404 handler
app.use((req, res) => {
sendNotFound(res, 'Endpoint not found'); // unified not-found response
});
// Graceful shutdown
process.on('SIGTERM', () => { // capture container shutdown event for graceful exit
logInfo('SIGTERM received, shutting down gracefully'); // log exit request for operators
if (server) { // only close when server was started to prevent undefined errors
server.close(() => { // close server to release port before process exits
logInfo('Server closed'); // confirm server has been closed for reliability
process.exit(0); // exit after server shutdown
});
} else { // handle case where server was never started (e.g. in tests)
process.exit(0); // exit immediately without server cleanup
}
});
let server; // holds HTTP server instance when started manually or via CLI
if (require.main === module) { // start server only when running this file directly
server = app.listen(port, '0.0.0.0', () => { // bind to all interfaces for demo usage
logInfo(`QMemory Demo App listening on port ${port}`); // log startup details for monitoring
logInfo('Environment:', process.env.NODE_ENV || 'development'); // log running mode for clarity
// Create some sample data in development
if (process.env.NODE_ENV !== 'production') { // avoid polluting production DB
storage.createUser({ username: 'demo', displayName: 'Demo User' }) // sample uses displayName to match route
.then(() => logInfo('Created demo user'))
.catch(err => logError('Failed to create demo user:', err));
}
});
}
module.exports = { app, server }; // export server for tests and app for external usage