UNPKG

@voilajsx/appkit

Version:

Minimal and framework agnostic Node.js toolkit designed for AI agentic backend development

1,867 lines (1,452 loc) • 67.4 kB
# VoilaJSX AppKit - LLM Guide: Core Philosophy & Universal Patterns šŸ¤– > **Essential foundation for AI agents to generate perfect VoilaJSX AppKit > code** ## CRITICAL: Read This First This section establishes the core patterns that ALL VoilaJSX AppKit code must follow. Every example is production-ready. Every pattern is tested. Every rule is absolute. --- ## WHEN TO USE VoilaJSX AppKit āœ… **ALWAYS use AppKit when:** - Building Node.js backend applications - Need authentication with roles/permissions - Require file storage, caching, queuing, or email - Want environment-driven configuration - Building multi-tenant or multi-org applications - Need production-ready security features - Want zero-configuration startup āŒ **NEVER use AppKit when:** - Building frontend React/Vue applications - Creating CLI tools or desktop applications - Need real-time WebSocket servers as primary feature - Building pure microservices with single responsibility - Working with non-Node.js environments --- ## COMPLETE MODULE REFERENCE (All 12 Modules) **MEMORIZE this table - it's your single source of truth:** | Module | Import | Class | Object Name | Optional Param | When to Use Param | | ------------ | ----------------------------------------------------------- | --------------- | ----------- | ----------------- | --------------------------------- | | **Util** | `import { utilClass } from '@voilajsx/appkit/util'` | `utilClass` | `util` | āŒ None | N/A | | **Config** | `import { configClass } from '@voilajsx/appkit/config'` | `configClass` | `config` | āŒ None | N/A | | **Auth** | `import { authClass } from '@voilajsx/appkit/auth'` | `authClass` | `auth` | āŒ None | N/A | | **Logger** | `import { loggerClass } from '@voilajsx/appkit/logger'` | `loggerClass` | `logger` | āœ… `(component?)` | Component-specific logging | | **Database** | `import { databaseClass } from '@voilajsx/appkit/database'` | `databaseClass` | `database` | āŒ None | N/A | | **Cache** | `import { cacheClass } from '@voilajsx/appkit/cache'` | `cacheClass` | `cache` | āœ… `(namespace?)` | Custom namespace (default: 'app') | | **Storage** | `import { storageClass } from '@voilajsx/appkit/storage'` | `storageClass` | `storage` | āŒ None | N/A | | **Queue** | `import { queueClass } from '@voilajsx/appkit/queue'` | `queueClass` | `queue` | āŒ None | N/A | | **Email** | `import { emailClass } from '@voilajsx/appkit/email'` | `emailClass` | `email` | āŒ None | N/A | | **Error** | `import { errorClass } from '@voilajsx/appkit/error'` | `errorClass` | `error` | āŒ None | N/A | | **Security** | `import { securityClass } from '@voilajsx/appkit/security'` | `securityClass` | `security` | āŒ None | N/A | | **Event** | `import { eventClass } from '@voilajsx/appkit/event'` | `eventClass` | `event` | āœ… `(namespace?)` | Namespace isolation for events | --- ## THE ONE FUNCTION RULE **EVERY module follows the same pattern:** ```javascript // ALWAYS use this pattern for EVERY module const module = moduleClass.get(); // Standard usage - MEMORIZE these exact patterns const util = utilClass.get(); const config = configClass.get(); const auth = authClass.get(); const database = databaseClass.get(); const storage = storageClass.get(); const queue = queueClass.get(); const email = emailClass.get(); const error = errorClass.get(); const security = securityClass.get(); // With optional parameters const logger = loggerClass.get('api'); // Component-specific const cache = cacheClass.get(); // Default 'app' namespace const userCache = cacheClass.get('users'); // Custom namespace const event = eventClass.get('notifications'); // Event namespace ``` **RULE: Never call constructors directly. Always use .get()** āŒ **NEVER do this:** ```javascript new AuthClass(); new DatabaseService(); new ConfigManager(); ``` āœ… **ALWAYS do this:** ```javascript const auth = authClass.get(); const database = databaseClass.get(); const config = configClass.get(); ``` --- ## MODULE CATEGORIES & DECISION TREE ### **Infrastructure (4 modules) - Use First** - **Auth**: JWT tokens, role-based permissions, middleware - **Database**: Multi-tenant database operations, ORM integration - **Security**: CSRF protection, rate limiting, input sanitization, encryption - **Error**: HTTP error handling, status codes, middleware ### **Data & Communication (5 modules) - Use Second** - **Cache**: Redis/Memory caching with namespaces - **Storage**: File storage (local/S3/R2), CDN integration - **Queue**: Background job processing, scheduled tasks - **Email**: Multi-provider email sending, templates - **Event**: Real-time events, pub/sub messaging ### **Developer Experience (3 modules) - Use Third** - **Util**: Safe object access, array operations, string utilities, performance helpers - **Config**: Environment variable parsing, application configuration - **Logger**: Structured logging, multiple transports --- ## IMPORT PATTERNS **ALWAYS use direct module imports for best tree-shaking:** āœ… **BEST (Perfect tree-shaking):** ```javascript import { utilClass } from '@voilajsx/appkit/util'; import { authClass } from '@voilajsx/appkit/auth'; import { configClass } from '@voilajsx/appkit/config'; ``` āœ… **GOOD (Still tree-shakable):** ```javascript import { utilClass, authClass, configClass } from '@voilajsx/appkit'; ``` āŒ **AVOID (Poor tree-shaking):** ```javascript import * as appkit from '@voilajsx/appkit'; ``` --- ## ENVIRONMENT-DRIVEN SCALING **AppKit automatically scales based on environment variables:** ### **Development (Zero Config)** ```bash # No environment variables needed npm start ``` - Memory cache/queue - Local file storage - Console logging - Single database ### **Production (Auto-Detection)** ```bash # Set these - everything scales automatically REDIS_URL=redis://... # → Distributed cache/queue DATABASE_URL=postgres://... # → Database logging/queue AWS_S3_BUCKET=bucket # → Cloud storage RESEND_API_KEY=re_... # → Email service ``` --- ## UNIVERSAL ERROR HANDLING PATTERNS ### **Pattern 1: Safe Access with Defaults** ```javascript // ALWAYS use util.get() for safe property access const util = utilClass.get(); āŒ const name = user.profile.name; // Can crash āœ… const name = util.get(user, 'profile.name', 'Guest'); // Never crashes ``` ### **Pattern 2: Try-Catch with Specific Errors** ```javascript const error = errorClass.get(); try { await someOperation(); } catch (err) { if (err.message.includes('validation')) { throw error.badRequest('Invalid input data'); } if (err.message.includes('permission')) { throw error.forbidden('Access denied'); } if (err.message.includes('not found')) { throw error.notFound('Resource not found'); } // Default to server error throw error.serverError('Operation failed'); } ``` ### **Pattern 3: Startup Validation** ```javascript // ALWAYS validate configuration at app startup try { const auth = authClass.get(); const config = configClass.get(); // Validate required config config.getRequired('database.url'); console.log('āœ… App validation passed'); } catch (error) { console.error('āŒ App validation failed:', error.message); process.exit(1); } ``` --- ## VARIABLE NAMING CONVENTIONS **ALWAYS use singular naming that matches the module name for clarity:** ### **Standard Pattern** ```javascript const util = utilClass.get(); // Singular: 'util' const config = configClass.get(); // Singular: 'config' const auth = authClass.get(); // Singular: 'auth' const database = databaseClass.get(); // Singular: 'database' const storage = storageClass.get(); // Singular: 'storage' const queue = queueClass.get(); // Singular: 'queue' const email = emailClass.get(); // Singular: 'email' const error = errorClass.get(); // Singular: 'error' const security = securityClass.get(); // Singular: 'security' const logger = loggerClass.get(); // Singular: 'logger' const cache = cacheClass.get(); // Singular: 'cache' + namespace const event = eventClass.get(); // Singular: 'event' ``` ### **Namespaced Instances** ```javascript // Logger, Cache, and Event allow parameters for organization // Logger - component-specific logging const logger = loggerClass.get('api'); const dbLogger = loggerClass.get('database'); // Cache - ALWAYS add namespace suffix const userCache = cacheClass.get('users'); const sessionCache = cacheClass.get('sessions'); // Event - namespace for event isolation const userEvent = eventClass.get('users'); const orderEvent = eventClass.get('orders'); ``` --- ## FRAMEWORK INTEGRATION PATTERNS ### **Express Pattern** ```javascript import express from 'express'; import { authClass, errorClass, loggerClass } from '@voilajsx/appkit'; const app = express(); const auth = authClass.get(); const error = errorClass.get(); const logger = loggerClass.get('app'); // ALWAYS use this middleware order app.use(express.json()); app.use(auth.requireLogin()); // Auth first app.use('/api', routes); // Routes second app.use(error.handleErrors()); // Error handling LAST // ALWAYS use asyncRoute wrapper app.post( '/users', error.asyncRoute(async (req, res) => { // Errors automatically handled }) ); ``` ### **Fastify Pattern** ```javascript import Fastify from 'fastify'; import { authClass, errorClass } from '@voilajsx/appkit'; const fastify = Fastify(); const auth = authClass.get(); const error = errorClass.get(); // ALWAYS set error handler fastify.setErrorHandler((error, request, reply) => { const appError = error.statusCode ? error : error.serverError(error.message); reply.status(appError.statusCode).send({ error: appError.type, message: appError.message, }); }); // ALWAYS use preHandler for auth fastify.get( '/protected', { preHandler: auth.requireRole('admin.tenant'), }, async (request, reply) => { // Route handler } ); ``` --- ## MODULE INTERACTION RULES ### **Dependency Order (ALWAYS follow this order):** 1. **Util** - No dependencies, use first 2. **Config** - No dependencies, use for configuration 3. **Logger** - Depends on Config 4. **Error** - Depends on Config and Logger 5. **Auth** - Depends on Config and Error 6. **Security** - Depends on Config and Error 7. **Database** - Depends on Config, Logger, and Error 8. **Cache** - Depends on Config and Logger 9. **Storage** - Depends on Config, Logger, and Error 10. **Email** - Depends on Config, Logger, and Error 11. **Queue** - Depends on Config, Logger, and Error 12. **Event** - Depends on Config, Logger, and Error ### **Safe Initialization Pattern** ```javascript // ALWAYS initialize in this order async function initializeApp() { try { // 1. Core utilities first const config = configClass.get(); const logger = loggerClass.get('init'); // 2. Validate configuration config.getRequired('database.url'); // 3. Initialize database const database = databaseClass.get(); // 4. Initialize other services const cache = cacheClass.get('app'); const queue = queueClass.get(); logger.info('āœ… App initialized successfully'); } catch (error) { console.error('āŒ App initialization failed:', error.message); process.exit(1); } } ``` --- ## TESTING PATTERNS ### **Module Reset Between Tests** ```javascript import { utilClass, loggerClass, cacheClass, configClass, } from '@voilajsx/appkit'; describe('App Tests', () => { afterEach(async () => { // ALWAYS reset module state between tests utilClass.clearCache(); await loggerClass.clear(); await cacheClass.clear(); configClass.clearCache(); }); test('should process data safely', () => { const util = utilClass.get(); const result = util.get({ user: { name: 'John' } }, 'user.name'); expect(result).toBe('John'); }); }); ``` --- ## PRODUCTION DEPLOYMENT PATTERNS ### **Environment Validation** ```javascript // ALWAYS validate environment at startup function validateProductionEnv() { if (process.env.NODE_ENV !== 'production') return; const required = ['VOILA_AUTH_SECRET', 'DATABASE_URL', 'REDIS_URL']; const missing = required.filter((key) => !process.env[key]); if (missing.length > 0) { console.error('āŒ Missing required environment variables:', missing); process.exit(1); } console.log('āœ… Production environment validated'); } ``` ### **Graceful Shutdown** ```javascript // ALWAYS implement graceful shutdown async function gracefulShutdown() { console.log('šŸ”„ Shutting down gracefully...'); try { await database.disconnect(); await queue.close(); await logger.flush(); console.log('āœ… Shutdown complete'); process.exit(0); } catch (error) { console.error('āŒ Shutdown error:', error); process.exit(1); } } process.on('SIGTERM', gracefulShutdown); process.on('SIGINT', gracefulShutdown); ``` # Essential Modules šŸš€ > **Core modules every application needs - Util, Config, Auth, Logger** ## šŸ› ļø UTIL MODULE - ALL 12 METHODS ### When to Use āœ… **Safe property access, array operations, string utilities, performance helpers** āŒ **Complex data transformations, DOM manipulation, heavy math** ### Core Pattern ```javascript import { utilClass } from '@voilajsx/appkit/util'; const util = utilClass.get(); ``` ### Complete API (All 12 Methods) ```javascript // 1. get() - Safe property access (NEVER crashes) const name = util.get(user, 'profile.name', 'Guest'); const items = util.get(response, 'data.items', []); const nested = util.get(obj, 'users[0].addresses[1].city', 'N/A'); // 2. isEmpty() - Universal empty check if (util.isEmpty(req.body.email)) throw error.badRequest('Email required'); util.isEmpty(null); // → true util.isEmpty({}); // → true util.isEmpty([]); // → true util.isEmpty(''); // → true util.isEmpty(' '); // → true (whitespace only) util.isEmpty(0); // → false (number is not empty) util.isEmpty(false); // → false (boolean is not empty) // 3. slugify() - URL-safe strings const slug = util.slugify('Product Name!'); // → 'product-name' const userSlug = util.slugify('User@Email.com'); // → 'user-email-com' // 4. chunk() - Split arrays into batches const batches = util.chunk(largeArray, 100); util.chunk([1, 2, 3, 4, 5, 6], 2); // → [[1,2], [3,4], [5,6]] // 5. debounce() - Prevent excessive function calls const debouncedSearch = util.debounce(searchAPI, 300); const saveSettings = util.debounce(saveToStorage, 1000); // 6. pick() - Extract specific object properties const publicUser = util.pick(user, ['id', 'name', 'email']); // 7. unique() - Remove duplicates const uniqueIds = util.unique([1, 2, 2, 3, 3, 4]); // → [1, 2, 3, 4] // 8. clamp() - Constrain numbers to range const volume = util.clamp(userInput, 0, 1); // Audio volume util.clamp(150, 0, 100); // → 100 (max limit) util.clamp(-10, 0, 100); // → 0 (min limit) // 9. formatBytes() - Human-readable file sizes const size = util.formatBytes(1048576); // → '1 MB' util.formatBytes(1024); // → '1 KB' // 10. truncate() - Smart text cutting const preview = util.truncate(longText, { length: 100, preserveWords: true }); // 11. sleep() - Promise-based delays await util.sleep(1000); // Wait 1 second // 12. uuid() - Generate unique identifiers const sessionId = util.uuid(); // → 'f47ac10b-58cc-4372-a567-0e02b2c3d479' ``` --- ## āš™ļø CONFIG MODULE ### When to Use āœ… **Environment variables, application settings, startup validation** āŒ **Runtime configuration changes, user preferences** ### Core Pattern ```javascript import { configClass } from '@voilajsx/appkit/config'; const config = configClass.get(); ``` ### Environment Variable Convention **UPPER_SNAKE_CASE** automatically becomes nested dot notation: ```bash # Environment Variable → Config Path DATABASE_HOST=localhost → config.get('database.host') DATABASE_CONNECTION_POOL_SIZE=10 → config.get('database.connection.pool_size') STRIPE_API_KEYS_PUBLIC=pk_test_123 → config.get('stripe.api.keys.public') FEATURES_ANALYTICS_ENABLED=true → config.get('features.analytics.enabled') ``` ### Essential Patterns ```javascript // 1. Required values (throws if missing) const dbUrl = config.getRequired('database.url'); const authSecret = config.getRequired('auth.secret'); // 2. Optional with defaults const port = config.get('server.port', 3000); const timeout = config.get('api.timeout', 5000); // 3. Environment detection const isDev = config.isDevelopment(); const isProd = config.isProduction(); // 4. Startup validation try { config.getRequired('database.url'); config.getRequired('auth.secret'); if (config.isProduction()) { config.getRequired('redis.url'); } console.log('āœ… Configuration validation passed'); } catch (error) { console.error('āŒ Configuration validation failed:', error.message); process.exit(1); } ``` --- ## šŸ” AUTH MODULE ### When to Use āœ… **Dual token system, JWT operations, role-level permissions, API security, password hashing** āŒ **Frontend authentication, OAuth providers, session storage** ### Core Pattern ```javascript import { authClass } from '@voilajsx/appkit/auth'; const auth = authClass.get(); ``` ### Dual Token System (CRITICAL - Two Distinct Types) ```javascript // āœ… LOGIN TOKENS - For user authentication (mobile/web) const loginToken = auth.generateLoginToken({ userId: 123, // REQUIRED - unique user identifier role: 'admin', // REQUIRED - role name (admin, user, moderator) level: 'tenant', // REQUIRED - level within role (basic, tenant, org, system) }, '7d'); // Short-medium expiry // āœ… API TOKENS - For service authentication (webhooks/integrations) const apiToken = auth.generateApiToken({ keyId: 'webhook_service', // REQUIRED - service identifier role: 'service', // REQUIRED - role name level: 'external', // REQUIRED - level within role }, '1y'); // Long expiry // āŒ WRONG - Don't mix these up auth.generateLoginToken({ keyId: 'test' }); // keyId is for API tokens auth.generateApiToken({ userId: 123 }); // userId is for login tokens ``` ### Role Hierarchy (Built-in Inheritance) ``` admin.system > admin.org > admin.tenant > moderator.manage > moderator.approve > moderator.review > user.max > user.pro > user.basic ``` ### Essential Auth Patterns ```javascript // 1. User route protection (login tokens only) app.get('/profile', auth.requireLoginToken(), handler); app.get('/admin', auth.requireLoginToken(), auth.requireUserRoles(['admin.tenant']), handler); // 2. API route protection (API tokens only) app.post('/webhook/data', auth.requireApiToken(), handler); // 3. Safe user extraction (works with both token types) const user = auth.user(req); if (!user) throw error.unauthorized('Authentication required'); // 4. Role hierarchy checking const userRoleLevel = `${user.role}.${user.level}`; if (!auth.hasRole(userRoleLevel, 'admin.tenant')) { throw error.forbidden('Admin access required'); } // 5. Permission checking (action:scope format) if (!auth.can(user, 'manage:tenant')) { throw error.forbidden('Insufficient permissions'); } // 6. Password handling const hashedPassword = await auth.hashPassword(plainPassword); const isValid = await auth.comparePassword(plainPassword, hashedPassword); // 7. Token operations (works with both types) const payload = auth.verifyToken(token); ``` ### Complete Authentication Flow ```javascript // User login endpoint (generates login token) app.post( '/auth/login', error.asyncRoute(async (req, res) => { const { email, password } = req.body; if (util.isEmpty(email)) throw error.badRequest('Email required'); if (util.isEmpty(password)) throw error.badRequest('Password required'); const user = await database.user.findUnique({ where: { email } }); if (!user) throw error.unauthorized('Invalid credentials'); const isValid = await auth.comparePassword(password, user.password); if (!isValid) throw error.unauthorized('Invalid credentials'); // Generate login token for user authentication const loginToken = auth.generateLoginToken({ userId: user.id, role: user.role, level: user.level, }); res.json({ token: loginToken, user: util.pick(user, ['id', 'email', 'name']), }); }) ); // API token creation endpoint (admin-only) app.post( '/admin/api-tokens', auth.requireLoginToken(), auth.requireUserRoles(['admin.tenant']), error.asyncRoute(async (req, res) => { const { keyId, permissions } = req.body; // Generate API token for service authentication const apiToken = auth.generateApiToken({ keyId, role: 'service', level: 'external', permissions, }, '1y'); // Store token info in database (store hash, not plain token) const hashedToken = await auth.hashPassword(apiToken); await database.apiToken.create({ data: { keyId, token: hashedToken, permissions }, }); res.json({ apiToken }); // Return once for client to save }) ); ``` --- ## šŸ“ LOGGER MODULE ### When to Use āœ… **Structured logging, error tracking, performance monitoring** āŒ **Simple console.log, debugging only** ### Core Pattern ```javascript import { loggerClass } from '@voilajsx/appkit/logger'; const logger = loggerClass.get(); ``` ### Auto-Transport Selection ```bash # Development → Console # Production with DATABASE_URL → Database # Production with REDIS_URL → Redis # Production with VOILA_LOGGER_HTTP_URL → HTTP endpoint ``` ### Essential Patterns ```javascript // 1. Component-specific loggers const logger = loggerClass.get('api'); const dbLogger = loggerClass.get('database'); // 2. Structured logging with context logger.info('User created', { userId: user.id, email: user.email, timestamp: Date.now(), }); // 3. Error logging try { await riskyOperation(); } catch (err) { logger.error('Operation failed', { error: err.message, stack: err.stack, userId: req.user?.id, }); throw error.serverError('Operation failed'); } // 4. Request logging middleware app.use((req, res, next) => { req.requestId = util.uuid(); req.logger = logger.child({ requestId: req.requestId, method: req.method, url: req.url, }); const startTime = Date.now(); res.on('finish', () => { req.logger.info('Request completed', { statusCode: res.statusCode, duration: Date.now() - startTime, }); }); next(); }); ``` --- ## ESSENTIAL MODULES INTEGRATION ```javascript // Complete API endpoint using all 4 essential modules import { utilClass } from '@voilajsx/appkit/util'; import { configClass } from '@voilajsx/appkit/config'; import { authClass } from '@voilajsx/appkit/auth'; import { loggerClass } from '@voilajsx/appkit/logger'; import { errorClass } from '@voilajsx/appkit/error'; const util = utilClass.get(); const config = configClass.get(); const auth = authClass.get(); const logger = loggerClass.get('api'); const error = errorClass.get(); // User profile update endpoint app.put( '/api/profile', auth.requireLogin(), error.asyncRoute(async (req, res) => { const user = auth.user(req); const requestLogger = logger.child({ userId: user.userId, requestId: util.uuid(), }); // Safe data extraction const name = util.get(req.body, 'name', '').trim(); const bio = util.get(req.body, 'bio', '').trim(); // Input validation if (util.isEmpty(name)) { requestLogger.warn('Profile update failed - empty name'); throw error.badRequest('Name is required'); } // Length validation from config const maxBioLength = config.get('user.bio.maxLength', 500); if (bio.length > maxBioLength) { throw error.badRequest(`Bio too long (max ${maxBioLength} characters)`); } // Create slug for profile URL const slug = util.slugify(name); // Update user data const updatedUser = await database.user.update({ where: { id: user.userId }, data: { name, bio: util.isEmpty(bio) ? null : bio, slug }, }); requestLogger.info('Profile updated successfully'); res.json({ success: true, user: util.pick(updatedUser, ['id', 'name', 'bio', 'slug']), }); }) ); ``` # Infrastructure Modules šŸ“Š > **Data persistence, communication, and background processing - Database, > Cache, Storage, Queue, Email** ## šŸ—„ļø DATABASE MODULE ### When to Use āœ… **Persistent data, user data, content, multi-tenant apps, cross-cloud orgs** āŒ **Temporary data, files, caching, session storage** ### Core Pattern ```javascript import { databaseClass } from '@voilajsx/appkit/database'; const database = databaseClass.get(); ``` ### Progressive Scaling (CRITICAL) ```javascript // Day 1: Single database const database = await databaseClass.get(); const users = await database.user.findMany(); // Month 6: Multi-tenant (zero code changes!) // Just add: VOILA_DB_TENANT=auto const database = await databaseClass.get(); // Now auto-filtered by tenant // Year 1: Multi-org (still zero code changes!) // Add: ORG_ACME=postgresql://acme.aws.com/db const acmeDatabase = await databaseClass.org('acme').get(); ``` ### Essential API (3 Core Patterns) ```javascript // 1. Normal user access (single or tenant-filtered) const database = await databaseClass.get(); const users = await database.user.findMany(); // Auto-filtered if tenant mode // 2. Admin access (all tenants) const dbTenants = await databaseClass.getTenants(); const allUsers = await dbTenants.user.findMany(); // Cross-tenant // 3. Organization-specific access const acmeDatabase = await databaseClass.org('acme').get(); const acmeUsers = await acmeDatabase.user.findMany(); ``` ### MANDATORY Schema Requirements ```sql -- āœ… EVERY table MUST include tenant_id from Day 1 (nullable for future) CREATE TABLE users ( id uuid PRIMARY KEY, email text UNIQUE, name text, tenant_id text, -- MANDATORY: nullable for future created_at timestamp DEFAULT now(), INDEX idx_users_tenant (tenant_id) -- MANDATORY: performance index ); CREATE TABLE posts ( id uuid PRIMARY KEY, title text, content text, user_id uuid REFERENCES users(id), tenant_id text, -- MANDATORY: on EVERY table created_at timestamp DEFAULT now(), INDEX idx_posts_tenant (tenant_id) -- MANDATORY: on EVERY table ); ``` --- ## šŸ’¾ CACHE MODULE ### When to Use āœ… **Speed up database queries, session data, API responses, computed results** āŒ **Permanent data, files, transactions, sensitive data without encryption** ### Core Pattern ```javascript import { cacheClass } from '@voilajsx/appkit/cache'; const cache = cacheClass.get(); // Default 'app' namespace ``` ### Auto-Strategy Detection ```bash # Development → Memory cache with LRU # Production → Redis (if REDIS_URL) → Memory ``` ### Essential API (5 Core Methods) ```javascript // 1. set() - Store with TTL (seconds) await cache.set('user:123', userData, 3600); // 1 hour TTL // 2. get() - Retrieve (null if not found/expired) const user = await cache.get('user:123'); // 3. getOrSet() - Get or compute and cache const weather = await cache.getOrSet( `weather:${city}`, async () => { return await fetchWeatherAPI(city); // Only runs on cache miss }, 1800 // 30 minutes ); // 4. delete() - Remove specific key await cache.delete('user:123'); // 5. clear() - Clear entire namespace await cache.clear(); ``` ### Namespace Isolation ```javascript // ALWAYS use specific namespaces - completely isolated const userCache = cacheClass.get('users'); const sessionCache = cacheClass.get('sessions'); const apiCache = cacheClass.get('external-api'); await userCache.set('123', userData); await sessionCache.set('123', sessionData); // Different from user:123 ``` --- ## šŸ“ STORAGE MODULE ### When to Use āœ… **File uploads, documents, images, videos, CDN integration** āŒ **Configuration data, temporary data, session storage** ### Core Pattern ```javascript import { storageClass } from '@voilajsx/appkit/storage'; const storage = storageClass.get(); ``` ### Auto-Strategy Detection ```bash # Development → Local files in ./uploads/ # Production → R2 (if CLOUDFLARE_R2_BUCKET) → S3 (if AWS_S3_BUCKET) → Local ``` ### Essential API (4 Core Methods) ```javascript // 1. put() - Upload files await storage.put('avatars/user123.jpg', imageBuffer); await storage.put('docs/contract.pdf', pdfBuffer, { contentType: 'application/pdf', cacheControl: 'public, max-age=3600', }); // 2. get() - Download files const buffer = await storage.get('avatars/user123.jpg'); if (!buffer) throw error.notFound('File not found'); // 3. delete() - Remove files await storage.delete('temp/old-file.jpg'); // 4. url() - Get public URLs const url = storage.url('avatars/user123.jpg'); // Local: /uploads/avatars/user123.jpg // S3: https://bucket.s3.region.amazonaws.com/avatars/user123.jpg // R2: https://cdn.example.com/avatars/user123.jpg ``` --- ## šŸš€ QUEUE MODULE ### When to Use āœ… **Background jobs, emails, file processing, webhooks, scheduled tasks** āŒ **Real-time operations, simple sync operations, immediate responses** ### Core Pattern ```javascript import { queueClass } from '@voilajsx/appkit/queue'; const queue = queueClass.get(); ``` ### Auto-Transport Detection ```bash # Development → Memory queue # Production → Redis (if REDIS_URL) → Database (if DATABASE_URL) → Memory ``` ### Essential API (3 Core Methods) ```javascript // 1. add() - Add jobs to queue await queue.add('email', { to: 'user@example.com', subject: 'Welcome!', body: 'Thanks for signing up', }); await queue.add( 'image-resize', { input: 'uploads/large.jpg', output: 'thumbnails/thumb.jpg', width: 200, }, { delay: 5000, // Start in 5 seconds attempts: 3, // Retry 3 times } ); // 2. process() - Handle jobs queue.process('email', async (data) => { await sendEmail(data.to, data.subject, data.body); return { sent: true }; }); queue.process('image-resize', async (data) => { await resizeImage(data.input, data.output, data.width); return { resized: true }; }); // 3. schedule() - Delayed jobs await queue.schedule( 'reminder', { userId: 123, message: 'Your trial ends soon!', }, 7 * 24 * 60 * 60 * 1000 ); // 7 days ``` --- ## šŸ“§ EMAIL MODULE ### When to Use āœ… **Transactional emails, notifications, templates, multi-provider support** āŒ **Marketing emails, bulk campaigns, newsletter management** ### Core Pattern ```javascript import { emailClass } from '@voilajsx/appkit/email'; const email = emailClass.get(); ``` ### Auto-Provider Detection ```bash # Production → Resend (if RESEND_API_KEY) → SMTP (if SMTP_HOST) → Console ``` ### Essential API (3 Core Methods) ```javascript // 1. send() - Basic email sending await email.send({ to: 'user@example.com', subject: 'Welcome to our app!', text: 'Thanks for signing up.', html: '<h1>Thanks for signing up!</h1>', }); // 2. sendTemplate() - Template-based emails await email.sendTemplate('welcome', { to: user.email, name: user.name, activationLink: `https://app.com/activate/${user.token}`, }); // 3. Queue integration (recommended for production) await queue.add('email', { template: 'password-reset', to: user.email, resetLink: resetUrl, }); queue.process('email', async (data) => { if (data.template) { return await email.sendTemplate(data.template, data); } else { return await email.send(data); } }); ``` --- ## INFRASTRUCTURE INTEGRATION ```javascript // File upload with complete infrastructure integration import { utilClass } from '@voilajsx/appkit/util'; import { authClass } from '@voilajsx/appkit/auth'; import { databaseClass } from '@voilajsx/appkit/database'; import { storageClass } from '@voilajsx/appkit/storage'; import { cacheClass } from '@voilajsx/appkit/cache'; import { queueClass } from '@voilajsx/appkit/queue'; import { errorClass } from '@voilajsx/appkit/error'; import { loggerClass } from '@voilajsx/appkit/logger'; const util = utilClass.get(); const auth = authClass.get(); const storage = storageClass.get(); const queue = queueClass.get(); const error = errorClass.get(); const logger = loggerClass.get('upload'); // File upload endpoint app.post( '/api/upload', auth.requireLogin(), upload.single('file'), error.asyncRoute(async (req, res) => { const user = auth.user(req); const userCache = cacheClass.get('users'); if (!req.file) { throw error.badRequest('No file uploaded'); } // 1. Store file in storage (auto-detects Local/S3/R2) const fileKey = `uploads/${user.userId}/${Date.now()}-${util.slugify(req.file.originalname)}`; await storage.put(fileKey, req.file.buffer, { contentType: req.file.mimetype, }); // 2. Save to database (auto-filtered by tenant if enabled) const database = await databaseClass.get(); const file = await database.file.create({ data: { key: fileKey, name: req.file.originalname, size: req.file.size, contentType: req.file.mimetype, userId: user.userId, }, }); // 3. Queue background processing await queue.add('process-upload', { fileId: file.id, fileKey: fileKey, userId: user.userId, }); // 4. Clear user's file cache await userCache.delete(`files:${user.userId}`); // 5. Log activity logger.info('File uploaded', { fileId: file.id, userId: user.userId, size: util.formatBytes(req.file.size), }); res.json({ success: true, file: { id: file.id, url: storage.url(fileKey), name: req.file.originalname, size: util.formatBytes(req.file.size), }, }); }) ); // Background file processor queue.process('process-upload', async (data) => { const database = await databaseClass.get(); const email = emailClass.get(); const processingLogger = loggerClass.get('processing'); try { // Get file buffer for processing const buffer = await storage.get(data.fileKey); // Process file (resize, convert, scan, etc.) const processedBuffer = await processFile(buffer); // Store processed version const processedKey = data.fileKey.replace('uploads/', 'processed/'); await storage.put(processedKey, processedBuffer); // Update database await database.file.update({ where: { id: data.fileId }, data: { processed: true, processedKey, processedAt: new Date(), }, }); // Clear cache const userCache = cacheClass.get('users'); await userCache.delete(`files:${data.userId}`); // Send notification email await queue.add('file-processed-email', { userId: data.userId, fileId: data.fileId, }); processingLogger.info('File processed successfully', { fileId: data.fileId, userId: data.userId, }); return { processed: true, processedKey }; } catch (error) { processingLogger.error('File processing failed', { fileId: data.fileId, error: error.message, }); throw error; } }); ``` # System Modules šŸ›”ļø > **Application security, error handling, and real-time communication - Error, > Security, Event** ## āš ļø ERROR MODULE ### When to Use āœ… **HTTP APIs, status codes, middleware integration, client responses** āŒ **CLI applications, non-HTTP servers, simple utilities** ### Core Pattern ```javascript import { errorClass } from '@voilajsx/appkit/error'; const error = errorClass.get(); ``` ### HTTP Status Code Mapping (CRITICAL) ```javascript // āœ… CORRECT - Use these exact error types for specific situations error.badRequest('message'); // 400 - Client input errors error.unauthorized('message'); // 401 - Authentication required error.forbidden('message'); // 403 - Access denied (user authenticated, no permission) error.notFound('message'); // 404 - Resource doesn't exist error.conflict('message'); // 409 - Business logic conflicts (duplicate email) error.serverError('message'); // 500 - Internal server errors // āŒ WRONG - Don't use wrong error types throw error.serverError('Email required'); // Should be badRequest throw error.badRequest('Database failed'); // Should be serverError throw error.unauthorized('Admin required'); // Should be forbidden ``` ### Essential API (4 Core Patterns) ```javascript // 1. Input validation (400 errors) if (!req.body.email) { throw error.badRequest('Email is required'); } if (!email.includes('@')) { throw error.badRequest('Invalid email format'); } // 2. Authentication checks (401 errors) if (!token) { throw error.unauthorized('Authentication token required'); } if (tokenExpired) { throw error.unauthorized('Session expired. Please login again.'); } // 3. Permission checks (403 errors) if (!user.isAdmin) { throw error.forbidden('Admin access required'); } if (user.status === 'suspended') { throw error.forbidden('Account suspended'); } // 4. Resource checks (404 errors) const user = await database.user.findUnique({ where: { id } }); if (!user) { throw error.notFound('User not found'); } ``` ### Framework Integration ```javascript // Express - Error handling middleware (MUST be last) app.use(error.handleErrors()); // Express - Async route wrapper app.post( '/users', error.asyncRoute(async (req, res) => { // Errors automatically handled if (!req.body.email) throw error.badRequest('Email required'); res.json({ success: true }); }) ); // Fastify - Error handler setup fastify.setErrorHandler((err, request, reply) => { const appError = err.statusCode ? err : error.serverError(err.message); reply.status(appError.statusCode).send({ error: appError.type, message: appError.message, }); }); ``` --- ## šŸ”’ SECURITY MODULE ### When to Use āœ… **Web forms, CSRF protection, rate limiting, input sanitization, encryption** āŒ **CLI applications, API-only services, read-only applications** ### Core Pattern ```javascript import { securityClass } from '@voilajsx/appkit/security'; const security = securityClass.get(); ``` ### Required Environment Variables ```bash # CRITICAL - Required for startup VOILA_SECURITY_CSRF_SECRET=your-csrf-secret-key-2024-minimum-32-chars VOILA_SECURITY_ENCRYPTION_KEY=64-char-hex-key-for-aes256-encryption ``` ### Essential API (4 Core Methods) ```javascript // 1. CSRF Protection (CRITICAL: Session middleware MUST come first) app.use(session({ secret: process.env.SESSION_SECRET })); app.use(security.forms()); // CSRF protection for all routes // Generate CSRF token for forms app.get('/form', (req, res) => { const csrfToken = req.csrfToken(); res.render('form', { csrfToken }); }); // 2. Rate Limiting app.use('/api', security.requests()); // Default: 100 requests per 15 minutes app.use('/auth', security.requests(5, 3600000)); // 5 requests per hour // 3. Input Sanitization const safeName = security.input(req.body.name, { maxLength: 50 }); const safeEmail = security.input(req.body.email?.toLowerCase()); const safeHtml = security.html(req.body.content, { allowedTags: ['p', 'b', 'i', 'a'], }); // 4. Data Encryption (AES-256-GCM) const encryptedSSN = security.encrypt(user.ssn); const encryptedPhone = security.encrypt(user.phone); // Decrypt for authorized access const originalSSN = security.decrypt(encryptedSSN); const originalPhone = security.decrypt(encryptedPhone); ``` --- ## šŸš€ EVENT MODULE ### When to Use āœ… **Real-time features, WebSocket connections, pub/sub messaging, live notifications** āŒ **HTTP APIs, file transfers, database operations, background jobs** ### Core Pattern ```javascript import { eventClass } from '@voilajsx/appkit/event'; const event = eventClass.get(); ``` ### Auto-Strategy Detection ```bash # Development → Memory-based event emitter # Production → Redis pub/sub (if REDIS_URL) → Memory ``` ### Essential API (6 Core Methods) ```javascript // 1. on() - Listen to events event.on('user.login', (data) => { console.log(`User ${data.userId} logged in`); }); // 2. emit() - Send events await event.emit('user.login', { userId: 123, timestamp: Date.now(), ip: req.ip, }); // 3. Wildcard patterns event.on('user.*', (eventName, data) => { console.log(`User event: ${eventName}`, data); }); // 4. once() - One-time listeners event.once('app.ready', () => { console.log('Application is ready'); }); // 5. off() - Remove listeners event.off('user.login'); // Remove all listeners event.off('user.login', specificHandler); // Remove specific listener // 6. namespace() - Isolated event channels const userEvent = eventClass.get('users'); const orderEvent = eventClass.get('orders'); userEvent.emit('created', data); // → users:created orderEvent.emit('created', data); // → orders:created ``` --- ## SYSTEM MODULES INTEGRATION ```javascript // Secure real-time application with all system modules import express from 'express'; import session from 'express-session'; import { createServer } from 'http'; import { Server } from 'socket.io'; import { utilClass } from '@voilajsx/appkit/util'; import { authClass } from '@voilajsx/appkit/auth'; import { errorClass } from '@voilajsx/appkit/error'; import { securityClass } from '@voilajsx/appkit/security'; import { eventClass } from '@voilajsx/appkit/event'; import { loggerClass } from '@voilajsx/appkit/logger'; const app = express(); const server = createServer(app); const io = new Server(server); const util = utilClass.get(); const auth = authClass.get(); const error = errorClass.get(); const security = securityClass.get(); const event = eventClass.get('app'); const logger = loggerClass.get('app'); // Security middleware setup (ORDER CRITICAL) app.use(express.json({ limit: '10mb' })); app.use( session({ secret: config.getRequired('session.secret'), resave: false, saveUninitialized: false, cookie: { secure: config.isProduction(), httpOnly: true }, }) ); app.use(security.forms()); // CSRF protection app.use('/api', security.requests(100, 900000)); // Rate limiting // Real-time secure messaging endpoint app.post( '/api/messages', auth.requireLogin(), error.asyncRoute(async (req, res) => { const user = auth.user(req); const { content, roomId } = req.body; // Input validation if (util.isEmpty(content)) { throw error.badRequest('Message content required'); } if (util.isEmpty(roomId)) { throw error.badRequest('Room ID required'); } // Sanitize content const safeContent = security.html(content, { allowedTags: ['b', 'i', 'em', 'strong'], maxLength: 1000, }); // Check permissions const database = databaseClass.get(); const room = await database.room.findFirst({ where: { id: roomId, members: { some: { userId: user.userId } }, }, }); if (!room) { throw error.forbidden('Access to room denied'); } // Save message const message = await database.message.create({ data: { content: safeContent, userId: user.userId, roomId, tenant_id: user.tenant_id, }, include: { user: { select: { id: true, name: true } }, }, }); // Emit real-time event await event.emit('message.created', { messageId: message.id, content: message.content, user: message.user, roomId, timestamp: message.createdAt, }); // Send via WebSocket io.to(`room:${roomId}`).emit('new-message', { id: message.id, content: message.content, user: message.user, timestamp: message.createdAt, }); logger.info('Message sent', { messageId: message.id, userId: user.userId, roomId, }); res.json({ success: true, message: { id: message.id, content: message.content, user: message.user, timestamp: message.createdAt, }, }); }) ); // WebSocket authentication and real-time handling io.use(async (socket, next) => { try { const token = socket.handshake.auth.token; const user = auth.verifyToken(token); socket.user = user; next(); } catch (err) { next(new Error('Authentication failed')); } }); io.on('connection', (socket) => { const user = socket.user; logger.info('User connected', { userId: user.userId, socketId: socket.id, }); // Join user to their rooms socket.join(`user:${user.userId}`); socket.on('join-room', async (roomId) => { // Verify user can join room const database = databaseClass.get(); const room = await database.room.findFirst({ where: { id: roomId, members: { some: { userId: user.userId } }, }, }); if (room) { await socket.join(`room:${roomId}`); socket.emit('joined-room', { roomId }); logger.info('User joined room', { userId: user.userId, roomId, }); } else { socket.emit('error', { message: 'Access denied to room' }); } }); socket.on('disconnect', () => { logger.info('User disconnected', { userId: user.userId }); }); }); // Event listeners for cross-service communication event.on('user.registered', async (data) => { // Send welcome notification io.to(`user:${data.userId}`).emit('notification', { type: 'welcome', message: 'Welcome to our platform!', timestamp: new Date(), }); }); // Error handling middleware (MUST be last) app.use(error.handleErrors()); server.listen(3000, () => { logger.info('šŸš€ Server started with real-time support', { port: 3000 }); }); ``` # Production & Complete Examples šŸš€ > **Production deployment, complete application examples, and comprehensive > guidance** ## ENVIRONMENT VALIDATION ### Production Environment Variables ```bash # āœ… Framework (Required in production) NODE_ENV=production VOILA_AUTH_SECRET=your-super-secure-jwt-secret-key-minimum-32-chars VOILA_SECURITY_CSRF_SECRET=your-csrf-secret-key-minimum-32-chars VOILA_SECURITY_ENCRYPTION_KEY=64-char-hex-encryption-key-for-aes256 # āœ… Services (Required) DATABASE_URL=postgresql://user:password@host:5432/database REDIS_URL=redis://user:password@host:6379 # āœ… Email (Choose one) RESEND_API_KEY=re_your_api_key # OR SMTP_HOST=smtp.gmail.com # āœ… Storage (Choose one) CLOUDFLARE_R2_BUCKET=your-bucket # OR AWS_S3_BUCKET=your-bucket # āœ… Application APP_NAME=Your Production App APP_URL=https://yourapp.com ``` ### Startup Validation Pattern ```javascript // ALWAYS validate environment at startup function validateProductionEnv() { if (process.env.NODE_ENV !== 'production') return; const required = ['VOILA_AUTH_SECRET', 'DATABASE_URL', 'REDIS_URL']; const missing = required.filter((key) => !process.env[key]); if (missing.length > 0) { console.error('āŒ Missing required environment variables:', missing); process.exit(1); } console.log('āœ… Production environment validated'); } // App initialization async function initializeApp() { try { validateProductionEnv(); const config = configClass.get(); const logger = loggerClass.get('init'); // Validate configuration config.getRequired('database.url'); config.getRequired('auth.secret'); // Initialize database const database = databaseClass.get(); await database.$queryRaw`SELECT 1`; logger.info('āœ… App initialized successfully'); } catch (error) { console.error('āŒ App initialization failed:', error.message); process.exit(1); } } ``` --- ## GRACEFUL SHUTDOWN ```javascript // ALWAYS implement graceful shutdown async function gracefulShutdown(signal) { console.log(`šŸ”„ Received ${signal}, shutting down gracefully...`); try { // Close server first (stop accepting new connections) if (server) { await new Promise((resolve) => server.close(resolve)); } // Close services in reverse dependency order await queueClass.get().close(); // Stop processing jobs await databaseClass.disconnect(); // Close DB connections await loggerClass.get().flush(); // Write remaining logs console.log('āœ… Graceful shutdown completed'); process.exit(0); } catch (error) { console.error('āŒ Shutdown error:', error); process.exit(1); } } process.on('SIGTERM', gracefulShutdown); process.on('SIGINT', gracefulShutdown); process.on('SIGUSR2', gracefulShutdown); // For PM2 ``` --- ## MODULE INITIALIZATION ORDER ```javascript // ALWAYS follow this exact order: // 1. Util (no dependencies) // 2. Config (no dependencies) // 3. Logger (depends on Config) // 4. Error (depends on Config + Logger) // 5. Auth (depends on Config + Error) // 6. Security (depends on Config + Error) // 7. Database (depends on Config + Logger + Error) // 8. Cache (depends on Config + Logger) // 9. Storage (depends on Config + Logger + Error) // 10. Email (depends on Config + Logger + Error) // 11. Queue (depends on Config + Logger + Error) // 12. Event (depends on Config + Logger + Error) async function initializeApp() { try { // 1-2. Core utilities first const config = configClass.get(); const util = utilClass.get(); // 3-4. Logging and error handling const logger = loggerClass.get('init'); const error = errorClass.get(); // 5-6. Security modules const auth = authClass.get(); const security = securityClass.get(); // 7. Database const database = databaseClass.get(); // 8-12. Infrastructure