From 663d890f3544314bca4e21ec359ca1ecd1da1e4f Mon Sep 17 00:00:00 2001 From: Krishiv Saini Date: Wed, 29 Oct 2025 19:57:05 +0530 Subject: [PATCH] feat: add environment variable validation - Add envValidator utility for backend - Validate required environment variables on startup - Add clear console feedback for missing/valid vars - Support default values for optional variables - Exit in production if required vars are missing - Continue with warnings in development mode - Improve developer experience and prevent config errors --- backend/index.js | 8 ++ backend/utils/envValidator.js | 214 ++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 backend/utils/envValidator.js diff --git a/backend/index.js b/backend/index.js index 84fc798..17ce6b3 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,9 +1,14 @@ /* eslint-disable no-undef */ const express = require("express"); require("dotenv").config(); +const { validateEnv } = require("./utils/envValidator"); const botRouter = require("./routes/botRoutes"); +const { errorHandler } = require("./middleware/validation"); const cors = require("cors"); +// Validate environment variables on startup +validateEnv(); + const app = express(); app.use( cors({ @@ -23,6 +28,9 @@ app.get("/", (req, res) => { res.send("Backend is Running!"); }); +// Error handling middleware (must be last) +app.use(errorHandler); + app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); }); diff --git a/backend/utils/envValidator.js b/backend/utils/envValidator.js new file mode 100644 index 0000000..4bc71e7 --- /dev/null +++ b/backend/utils/envValidator.js @@ -0,0 +1,214 @@ +/** + * Environment Variable Validation Utility + * Validates required environment variables on application startup + */ + +/** + * Configuration for required environment variables + */ +const envConfig = { + required: [ + { + name: 'NODE_ENV', + description: 'Application environment (development, production, test)', + defaultValue: 'development', + }, + { + name: 'SERVER_PORT', + description: 'Port number for the server', + defaultValue: '3000', + }, + ], + optional: [ + { + name: 'FRONTEND_URL', + description: 'URL of the frontend application', + defaultValue: 'http://localhost:5173', + }, + { + name: 'GEMINI_API_KEY', + description: 'Google Gemini API key for chatbot', + }, + { + name: 'DATABASE_URL', + description: 'Database connection string', + }, + { + name: 'JWT_SECRET', + description: 'Secret key for JWT token generation', + }, + { + name: 'EMAIL_HOST', + description: 'SMTP host for sending emails', + }, + { + name: 'EMAIL_PORT', + description: 'SMTP port', + }, + { + name: 'EMAIL_USER', + description: 'Email username', + }, + { + name: 'EMAIL_PASSWORD', + description: 'Email password', + }, + ], +}; + +/** + * Validate a single environment variable + */ +const validateEnvVar = (config, isRequired) => { + const value = process.env[config.name]; + + if (!value) { + if (config.defaultValue) { + process.env[config.name] = config.defaultValue; + return { + status: 'default', + name: config.name, + value: config.defaultValue, + description: config.description, + }; + } + + return { + status: isRequired ? 'missing' : 'optional', + name: config.name, + description: config.description, + }; + } + + return { + status: 'valid', + name: config.name, + value: value.substring(0, 20) + (value.length > 20 ? '...' : ''), + description: config.description, + }; +}; + +/** + * Validate all environment variables + */ +const validateEnv = () => { + console.log('\nšŸ” Validating environment variables...\n'); + + const results = { + valid: [], + defaults: [], + missing: [], + optional: [], + }; + + // Check required variables + envConfig.required.forEach((config) => { + const result = validateEnvVar(config, true); + + if (result.status === 'valid') { + results.valid.push(result); + } else if (result.status === 'default') { + results.defaults.push(result); + } else if (result.status === 'missing') { + results.missing.push(result); + } + }); + + // Check optional variables + envConfig.optional.forEach((config) => { + const result = validateEnvVar(config, false); + + if (result.status === 'valid') { + results.valid.push(result); + } else if (result.status === 'default') { + results.defaults.push(result); + } else { + results.optional.push(result); + } + }); + + // Display results + if (results.valid.length > 0) { + console.log('āœ“ Valid environment variables:'); + results.valid.forEach((item) => { + console.log(` āœ“ ${item.name} - ${item.description}`); + }); + console.log(''); + } + + if (results.defaults.length > 0) { + console.log('⚠ Using default values:'); + results.defaults.forEach((item) => { + console.log(` ⚠ ${item.name} = ${item.value} (${item.description})`); + }); + console.log(''); + } + + if (results.optional.length > 0) { + console.log('ℹ Optional variables not set:'); + results.optional.forEach((item) => { + console.log(` ℹ ${item.name} - ${item.description}`); + }); + console.log(''); + } + + if (results.missing.length > 0) { + console.log('āœ– Missing required environment variables:'); + results.missing.forEach((item) => { + console.log(` āœ– ${item.name} - ${item.description}`); + }); + console.log(''); + console.log('Please set the missing environment variables in your .env file'); + console.log('See .env.example for reference\n'); + + if (process.env.NODE_ENV === 'production') { + process.exit(1); + } else { + console.log('Running in development mode - continuing with warnings\n'); + } + } else { + console.log('āœ“ All required environment variables are set!\n'); + } + + return results; +}; + +/** + * Generate .env.example file based on config + */ +const generateEnvExample = () => { + const lines = [ + '# Environment Variables Configuration', + '# Copy this file to .env and fill in your values', + '', + '# Required Variables', + '', + ]; + + envConfig.required.forEach((config) => { + lines.push(`# ${config.description}`); + if (config.defaultValue) { + lines.push(`${config.name}=${config.defaultValue}`); + } else { + lines.push(`${config.name}=`); + } + lines.push(''); + }); + + lines.push('# Optional Variables'); + lines.push(''); + + envConfig.optional.forEach((config) => { + lines.push(`# ${config.description}`); + lines.push(`${config.name}=`); + lines.push(''); + }); + + return lines.join('\n'); +}; + +module.exports = { + validateEnv, + generateEnvExample, + envConfig, +};