diff --git a/apps/webapp/app/layout.tsx b/apps/webapp/app/layout.tsx index be22dab..b57976c 100644 --- a/apps/webapp/app/layout.tsx +++ b/apps/webapp/app/layout.tsx @@ -5,6 +5,7 @@ import "./globals.css"; import { Toaster } from "@/components/ui/toaster"; import { ErrorBoundary } from "@/components/error/ErrorBoundary"; import { ErrorProvider, setupGlobalErrorHandling } from "@/components/error/ErrorProvider"; +import { EnvironmentLogger } from "@/components/debug/EnvironmentLogger"; // Set up global error handling and performance monitoring if (typeof window !== 'undefined') { @@ -48,6 +49,7 @@ export default function RootLayout({ return ( + {children} diff --git a/apps/webapp/components/debug/EnvironmentLogger.tsx b/apps/webapp/components/debug/EnvironmentLogger.tsx new file mode 100644 index 0000000..a18a901 --- /dev/null +++ b/apps/webapp/components/debug/EnvironmentLogger.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { useEffect } from 'react'; + +interface EnvironmentLoggerProps { + enabled?: boolean; +} + +export function EnvironmentLogger({ enabled = true }: EnvironmentLoggerProps) { + useEffect(() => { + if (!enabled) { + return; + } + + // Get all environment variables accessible on the client side + const clientEnvVars: Record = {}; + + // In Next.js, only environment variables prefixed with NEXT_PUBLIC_ are available on the client + // We'll also check process.env for any other variables that might be exposed + for (const key in process.env) { + if (key.startsWith('NEXT_PUBLIC_') || process.env[key] !== undefined) { + clientEnvVars[key] = process.env[key] || ''; + } + } + + // Also log some runtime information that might be useful for debugging + const debugInfo = { + timestamp: new Date().toISOString(), + userAgent: typeof window !== 'undefined' ? window.navigator.userAgent : 'SSR', + currentUrl: typeof window !== 'undefined' ? window.location.href : 'SSR', + nodeEnv: process.env.NODE_ENV, + environmentVariables: clientEnvVars, + }; + + console.group('šŸ” Client-side Environment Debug Info'); + console.log('Timestamp:', debugInfo.timestamp); + console.log('Node Environment:', debugInfo.nodeEnv); + console.log('User Agent:', debugInfo.userAgent); + console.log('Current URL:', debugInfo.currentUrl); + console.log('Environment Variables Count:', Object.keys(clientEnvVars).length); + + console.group('šŸ“‹ Environment Variables:'); + Object.entries(clientEnvVars).forEach(([key, value]) => { + // Mask sensitive values for security + const maskedValue = key.toLowerCase().includes('key') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token') + ? value.substring(0, 4) + '****' + value.substring(value.length - 4) + : value; + console.log(`${key}:`, maskedValue); + }); + console.groupEnd(); + + console.log('Full debug object:', debugInfo); + console.groupEnd(); + + }, [enabled]); + + // This component doesn't render anything visible + return null; +} \ No newline at end of file diff --git a/apps/webapp/instrumentation.ts b/apps/webapp/instrumentation.ts new file mode 100644 index 0000000..3700538 --- /dev/null +++ b/apps/webapp/instrumentation.ts @@ -0,0 +1,15 @@ +/** + * Next.js instrumentation file + * This runs when the Next.js server starts up + */ + +export async function register() { + // Only run instrumentation in development mode + if (process.env.NODE_ENV === 'development') { + // Dynamic import to avoid bundling in production + const { initializeServerEnvironmentLogging } = await import('./lib/debug/ServerEnvironmentLogger'); + + // Initialize server environment logging + initializeServerEnvironmentLogging(); + } +} \ No newline at end of file diff --git a/apps/webapp/lib/debug/ServerEnvironmentLogger.ts b/apps/webapp/lib/debug/ServerEnvironmentLogger.ts new file mode 100644 index 0000000..451c22c --- /dev/null +++ b/apps/webapp/lib/debug/ServerEnvironmentLogger.ts @@ -0,0 +1,156 @@ +/** + * Server-side environment logger for debugging during development + * Only runs in development mode and logs environment variables at server startup + */ + +interface EnvironmentCategory { + name: string; + icon: string; + variables: Record; +} + +interface ServerEnvironmentInfo { + timestamp: string; + workingDirectory: string; + nodeEnv: string; + categories: EnvironmentCategory[]; + totalCount: number; +} + +/** + * Masks sensitive values in environment variables + */ +function maskSensitiveValue(key: string, value: string): string { + const sensitiveKeys = ['key', 'secret', 'token', 'password', 'auth']; + const isSensitive = sensitiveKeys.some(sensitiveKey => + key.toLowerCase().includes(sensitiveKey) + ); + + if (!isSensitive || !value || value.length < 8) { + return value; + } + + return value.substring(0, 4) + '****' + value.substring(value.length - 4); +} + +/** + * Categorizes environment variables into logical groups + */ +function categorizeEnvironmentVariables(): EnvironmentCategory[] { + const env = process.env; + const categories: EnvironmentCategory[] = []; + + // NEXT_PUBLIC_* variables + const nextPublicVars: Record = {}; + + // Firebase-related variables + const firebaseVars: Record = {}; + + // Other development variables + const otherVars: Record = {}; + + for (const [key, value] of Object.entries(env)) { + if (!value) continue; + + const maskedValue = maskSensitiveValue(key, value); + + if (key.startsWith('NEXT_PUBLIC_')) { + nextPublicVars[key] = maskedValue; + } else if (key.includes('FIREBASE') || key.includes('FIRE_')) { + firebaseVars[key] = maskedValue; + } else if (['NODE_ENV', 'PORT', 'HOSTNAME', 'PWD'].includes(key)) { + otherVars[key] = maskedValue; + } + } + + if (Object.keys(nextPublicVars).length > 0) { + categories.push({ + name: 'Next.js Public Variables', + icon: '🌐', + variables: nextPublicVars + }); + } + + if (Object.keys(firebaseVars).length > 0) { + categories.push({ + name: 'Firebase Variables', + icon: 'šŸ”„', + variables: firebaseVars + }); + } + + if (Object.keys(otherVars).length > 0) { + categories.push({ + name: 'Development Variables', + icon: 'āš™ļø', + variables: otherVars + }); + } + + return categories; +} + +/** + * Gets comprehensive server environment information + */ +function getServerEnvironmentInfo(): ServerEnvironmentInfo { + const categories = categorizeEnvironmentVariables(); + const totalCount = categories.reduce((sum, cat) => sum + Object.keys(cat.variables).length, 0); + + return { + timestamp: new Date().toISOString(), + workingDirectory: process.cwd(), + nodeEnv: process.env.NODE_ENV || 'unknown', + categories, + totalCount + }; +} + +/** + * Logs server environment information to the console + * Only runs in development mode + */ +export function logServerEnvironment(): void { + // Only log in development mode + if (process.env.NODE_ENV !== 'development') { + return; + } + + const envInfo = getServerEnvironmentInfo(); + + console.log('\n' + '='.repeat(60)); + console.group('šŸ–„ļø Server-Side Environment Debug Info'); + console.log('šŸ“… Timestamp:', envInfo.timestamp); + console.log('šŸŒ Node Environment:', envInfo.nodeEnv); + console.log('šŸ“ Working Directory:', envInfo.workingDirectory); + console.log('šŸ“Š Total Environment Variables:', envInfo.totalCount); + console.log(''); + + // Log each category + envInfo.categories.forEach(category => { + console.group(`${category.icon} ${category.name} (${Object.keys(category.variables).length})`); + + Object.entries(category.variables).forEach(([key, value]) => { + console.log(` ${key}:`, value); + }); + + console.groupEnd(); + }); + + console.groupEnd(); + console.log('='.repeat(60) + '\n'); +} + +/** + * Initialize server environment logging + * Call this at server startup to log environment variables + */ +export function initializeServerEnvironmentLogging(): void { + if (process.env.NODE_ENV === 'development') { + // Log immediately when called + logServerEnvironment(); + + // Also log a startup message + console.log('šŸš€ Server-side environment logging initialized for development mode'); + } +} \ No newline at end of file diff --git a/apps/webapp/src/__tests__/ServerEnvironmentLogger.test.ts b/apps/webapp/src/__tests__/ServerEnvironmentLogger.test.ts new file mode 100644 index 0000000..2f1efdb --- /dev/null +++ b/apps/webapp/src/__tests__/ServerEnvironmentLogger.test.ts @@ -0,0 +1,113 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; + +describe('ServerEnvironmentLogger', () => { + let consoleGroupSpy: ReturnType; + let consoleLogSpy: ReturnType; + let consoleGroupEndSpy: ReturnType; + + beforeEach(() => { + // Mock console methods + consoleGroupSpy = vi.spyOn(console, 'group').mockImplementation(() => {}); + consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + consoleGroupEndSpy = vi.spyOn(console, 'groupEnd').mockImplementation(() => {}); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('logServerEnvironment', () => { + it('should not log anything when NODE_ENV is not development', async () => { + // Set NODE_ENV to production + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + + const { logServerEnvironment } = await import('../../lib/debug/ServerEnvironmentLogger'); + + logServerEnvironment(); + + expect(consoleGroupSpy).not.toHaveBeenCalled(); + expect(consoleLogSpy).not.toHaveBeenCalled(); + + // Restore original NODE_ENV + process.env.NODE_ENV = originalNodeEnv; + }); + + it('should log environment info when NODE_ENV is development', async () => { + // Set NODE_ENV to development + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + // Set some test environment variables + process.env.NEXT_PUBLIC_TEST = 'test-value'; + process.env.FIREBASE_API_KEY = 'test-firebase-key'; + + const { logServerEnvironment } = await import('../../lib/debug/ServerEnvironmentLogger'); + + logServerEnvironment(); + + expect(consoleGroupSpy).toHaveBeenCalledWith('šŸ–„ļø Server-Side Environment Debug Info'); + expect(consoleLogSpy).toHaveBeenCalledWith('šŸŒ Node Environment:', 'development'); + expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('šŸ“ Working Directory:'), expect.any(String)); + + // Clean up + delete process.env.NEXT_PUBLIC_TEST; + delete process.env.FIREBASE_API_KEY; + process.env.NODE_ENV = originalNodeEnv; + }); + + it('should mask sensitive environment variables', async () => { + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + // Set a sensitive environment variable + process.env.NEXT_PUBLIC_API_KEY = 'very-secret-api-key-value'; + + const { logServerEnvironment } = await import('../../lib/debug/ServerEnvironmentLogger'); + + logServerEnvironment(); + + // Check that the sensitive value was masked + const calls = consoleLogSpy.mock.calls; + const keyCall = calls.find(call => call[0] === ' NEXT_PUBLIC_API_KEY:'); + expect(keyCall).toBeDefined(); + if (keyCall) { + expect(keyCall[1]).toMatch(/^very\*\*\*\*alue$/); + } + + // Clean up + delete process.env.NEXT_PUBLIC_API_KEY; + process.env.NODE_ENV = originalNodeEnv; + }); + }); + + describe('initializeServerEnvironmentLogging', () => { + it('should call logServerEnvironment in development mode', async () => { + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + const { initializeServerEnvironmentLogging } = await import('../../lib/debug/ServerEnvironmentLogger'); + + initializeServerEnvironmentLogging(); + + expect(consoleGroupSpy).toHaveBeenCalled(); + expect(consoleLogSpy).toHaveBeenCalledWith('šŸš€ Server-side environment logging initialized for development mode'); + + process.env.NODE_ENV = originalNodeEnv; + }); + + it('should not log anything in non-development mode', async () => { + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + + const { initializeServerEnvironmentLogging } = await import('../../lib/debug/ServerEnvironmentLogger'); + + initializeServerEnvironmentLogging(); + + expect(consoleGroupSpy).not.toHaveBeenCalled(); + expect(consoleLogSpy).not.toHaveBeenCalled(); + + process.env.NODE_ENV = originalNodeEnv; + }); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index 40001a9..66d9273 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "packages/*" ], "scripts": { - "dev:webapp": "mkdir -p logs && cd apps/webapp && FORCE_COLOR=1 pnpm dev 2>&1 | tee >(while IFS= read -r line; do echo \"[$(date '+%Y-%m-%d %H:%M:%S')] $line\"; done > ../../logs/webapp-dev.$(date '+%Y-%m-%d-%H').log)", - "dev:mcp-api": "mkdir -p logs && cd apps/mcp-api && FORCE_COLOR=1 pnpm dev 2>&1 | tee >(while IFS= read -r line; do echo \"[$(date '+%Y-%m-%d %H:%M:%S')] $line\"; done > ../../logs/mcp-api-dev.$(date '+%Y-%m-%d-%H').log)", - "dev:emulators:with-data": "mkdir -p logs && firebase emulators:start --import=.data/emulators/firebase-data --only auth,firestore 2>&1 | tee logs/firebase-emulators.$(date '+%Y-%m-%d-%H').log", + "dev:webapp": "mkdir -p logs && cd apps/webapp && FORCE_COLOR=1 pnpm dev 2>&1 | tee >(while IFS= read -r line; do echo \"[$(date '+%Y-%m-%d %H:%M:%S')] $line\"; done > ../../logs/webapp-dev.$(date '+%Y-%m-%dT%H').log)", + "dev:mcp-api": "mkdir -p logs && cd apps/mcp-api && FORCE_COLOR=1 pnpm dev 2>&1 | tee >(while IFS= read -r line; do echo \"[$(date '+%Y-%m-%d %H:%M:%S')] $line\"; done > ../../logs/mcp-api-dev.$(date '+%Y-%m-%dT%H').log)", + "dev:emulators:with-data": "mkdir -p logs && firebase emulators:start --import=.data/emulators/firebase-data --only auth,firestore 2>&1 | tee logs/firebase-emulators.$(date '+%Y-%m-%dT%H').log", "build": "turbo build", "lint": "turbo lint", "lint:fix": "turbo lint:fix", diff --git a/specs/nextjs-builtin-env-debugging.md b/specs/nextjs-builtin-env-debugging.md new file mode 100644 index 0000000..8302187 --- /dev/null +++ b/specs/nextjs-builtin-env-debugging.md @@ -0,0 +1,249 @@ +# Next.js Built-in Environment Variable Debugging Research + +**Date:** 2025-07-21 +**Research Context:** Investigation of Next.js built-in debugging features for environment variables during development + +## Executive Summary + +After comprehensive research into Next.js documentation and features, Next.js provides **limited built-in debugging capabilities** specifically for environment variables. The framework focuses primarily on general request logging, build debugging, and performance profiling rather than dedicated environment variable inspection tools. + +## Key Findings + +### āŒ What Next.js Does NOT Provide Built-in + +- **No dedicated CLI flags** for environment variable debugging +- **No built-in environment variable inspector** in development mode +- **No automatic logging** of environment variables during startup +- **No built-in validation** or verification tools for environment variables +- **No dedicated dev server features** for environment variable inspection + +### āœ… What Next.js DOES Provide Built-in + +1. **Basic CLI Debug Flags** (Limited Relevance) + - `--debug-prerender` - Debug prerender errors (development only) + - `--debug` / `-d` - Enable verbose build output + - `next info --verbose` - System information for bug reports + +2. **Development Server Logging Configuration** + ```javascript + // next.config.js + module.exports = { + logging: { + fetches: { + fullUrl: true, // Log full URLs for fetch requests + hmrRefreshes: true // Log HMR cache refreshes + }, + incomingRequests: true // Log all incoming requests (default) + } + } + ``` + +3. **Environment Variable Load Order** (Debugging Aid) + - Predictable loading sequence helps troubleshoot configuration issues + - Order: `process.env` → `.env.$(NODE_ENV).local` → `.env.local` → `.env.$(NODE_ENV)` → `.env` + +4. **Automatic NODE_ENV Assignment** + - `development` for `next dev` + - `production` for other commands + - Ensures consistent environment detection + +## Practical Environment Variable Debugging Methods + +Since Next.js lacks built-in debugging tools, developers must use manual approaches: + +### Server-Side Inspection + +```javascript +// In API routes, getServerSideProps, or getStaticProps +export async function getServerSideProps() { + // Debug all environment variables + console.log('All environment variables:', process.env); + + // Debug specific variables + console.log('Firebase Config:', { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + }); + + // Debug feature flags + if (process.env.FEATURE_NEW_DASHBOARD === 'true') { + console.log('New Dashboard feature is enabled'); + } + + return { props: {} }; +} +``` + +### API Route Debugging + +```javascript +// pages/api/debug-env.js or app/api/debug-env/route.js +export default function handler(req, res) { + const envVars = { + nodeEnv: process.env.NODE_ENV, + // Only log non-sensitive variables + publicVars: Object.keys(process.env) + .filter(key => key.startsWith('NEXT_PUBLIC_')) + .reduce((acc, key) => { + acc[key] = process.env[key]; + return acc; + }, {}) + }; + + console.log('Environment Debug:', envVars); + res.json(envVars); +} +``` + +### Development Environment File Strategy + +```bash +# .env.development (for development-specific debugging) +DEBUG_MODE=true +LOG_LEVEL=debug +NEXT_PUBLIC_ENV_CHECK=development + +# .env.development.local (for local overrides, git-ignored) +NEXT_PUBLIC_FIREBASE_API_KEY=your_local_key +DEBUG_VERBOSE=true +``` + +## Advanced Debugging Techniques + +### 1. Using @next/env Package + +```javascript +// For testing or external scripts +import { loadEnvConfig } from '@next/env'; + +const projectDir = process.cwd(); +loadEnvConfig(projectDir); + +console.log('Loaded environment:', process.env.NEXT_PUBLIC_FIREBASE_API_KEY); +``` + +### 2. VS Code Debugging Configuration + +```json +{ + "type": "node", + "request": "launch", + "name": "Debug Next.js", + "program": "${workspaceFolder}/node_modules/.bin/next", + "args": ["dev"], + "console": "integratedTerminal", + "serverReadyAction": { + "pattern": "started server on .+, url: (https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } +} +``` + +### 3. Server-Side Debugging with Chrome DevTools + +```bash +# Enable Node.js debugging +NODE_OPTIONS='--inspect' npm run dev + +# For remote debugging (e.g., Docker) +NODE_OPTIONS='--inspect=0.0.0.0' npm run dev +``` + +## Limitations and Gaps + +### Critical Limitations + +1. **No Built-in Validation** + - No automatic checking for required environment variables + - No warnings for missing or malformed values + - No type validation for environment variables + +2. **No Development UI** + - No built-in interface to view loaded environment variables + - No visual inspection tools in development mode + - No real-time environment variable monitoring + +3. **Limited Error Messages** + - Environment variable errors often result in generic failures + - Difficult to trace which specific variable is missing or incorrect + - No helpful debugging suggestions in error messages + +4. **Client-Side Blindness** + - Server-side environment variables are invisible on client + - No built-in way to verify client-side variable availability + - NEXT_PUBLIC_ prefix requirement not enforced or validated + +### Security Considerations + +Next.js intentionally lacks environment variable debugging tools in production: +- Prevents accidental exposure of sensitive information +- No built-in endpoints that could leak environment data +- Requires manual implementation of debug endpoints (good for security) + +## Recommendations for This Project + +### Immediate Actions + +1. **Implement Custom Environment Logger** + - Create a development-only component that logs environment variables + - Include validation for required variables + - Add startup warnings for missing configurations + +2. **Add Development Debug Route** + ```javascript + // app/api/debug/env/route.js (development only) + export async function GET() { + if (process.env.NODE_ENV !== 'development') { + return Response.json({ error: 'Not available in production' }, { status: 404 }); + } + + return Response.json({ + nodeEnv: process.env.NODE_ENV, + publicVars: Object.keys(process.env) + .filter(key => key.startsWith('NEXT_PUBLIC_')) + .reduce((acc, key) => ({ ...acc, [key]: process.env[key] }), {}) + }); + } + ``` + +3. **Enhanced Package.json Scripts** + ```json + { + "scripts": { + "dev:debug": "NODE_OPTIONS='--inspect' next dev", + "dev:verbose": "DEBUG=* next dev", + "env:check": "node scripts/check-env.js" + } + } + ``` + +### Long-term Considerations + +Given the limitations of Next.js built-in features, consider: + +1. **Third-party Environment Validation Libraries** + - `zod` for runtime type validation + - `dotenv-safe` for required variable checking + - `env-var` for type-safe environment variable parsing + +2. **Custom Development Tools** + - Build internal debugging utilities + - Create development-only environment variable inspection UI + - Implement startup validation checks + +## Conclusion + +**Next.js does not provide comprehensive built-in environment variable debugging features.** The framework's philosophy appears to rely on: + +1. **Manual logging** through `console.log()` statements +2. **Configuration management** through predictable file loading +3. **External tooling** for validation and inspection + +This approach prioritizes security and simplicity over developer convenience. For robust environment variable debugging, custom implementation is required. + +The built-in features are sufficient for basic development but inadequate for complex applications with many environment variables or teams needing systematic debugging capabilities. + +--- + +**Next Steps:** Implement custom environment variable debugging tools based on the practical examples provided above. \ No newline at end of file diff --git a/specs/simple-env-debugging-approaches.md b/specs/simple-env-debugging-approaches.md new file mode 100644 index 0000000..29a0180 --- /dev/null +++ b/specs/simple-env-debugging-approaches.md @@ -0,0 +1,665 @@ +# Simple Environment Variable Debugging Approaches + +## Overview + +This document provides simple, lightweight approaches for debugging environment variables in Next.js applications without complex custom logging systems. All approaches are designed to work reliably in development environments and provide immediate feedback. + +## Quick Summary Table + +| Approach | Use Case | Complexity | Client/Server | Best For | +|----------|----------|------------|---------------|----------| +| Console.log in Pages | Quick server-side checks | Low | Server | Development startup debugging | +| API Route Inspection | Structured server env viewing | Low | Server | Comprehensive server env analysis | +| Next.js Middleware | Request-based debugging | Medium | Server | Route-specific env checking | +| React Hook | Client-side env debugging | Medium | Client | Interactive client debugging | +| Browser Console Helpers | Manual debugging | Low | Client | Ad-hoc testing and debugging | +| Floating Debug Component | Visual env display | Medium | Client | Real-time env monitoring | + +--- + +## 1. Console.log-based Approaches + +### 1.1 Simple Page-Level Console Logging + +**Best for:** Quick server-side environment checks during page load + +```tsx +// In any page component or server component +export default function Page() { + // Server-side console logging (appears in terminal) + console.log('šŸ” Server Environment Check:', { + nodeEnv: process.env.NODE_ENV, + firebaseProject: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + timestamp: new Date().toISOString(), + }); + + return
Your page content
; +} +``` + +**Pros:** +- Immediate feedback in terminal +- Zero configuration required +- Works in all Next.js page types +- Simple copy-paste implementation + +**Cons:** +- Only shows during page load +- Limited to server-side variables +- Logs appear in terminal, not browser + +--- + +## 2. API Route Approaches + +### 2.1 Comprehensive Environment API Route + +**Best for:** Structured server-side environment inspection + +**Implementation:** +```typescript +// app/api/debug/env/route.ts +import { NextResponse } from 'next/server'; + +export async function GET() { + // Only allow this in development + if (process.env.NODE_ENV !== 'development') { + return NextResponse.json( + { error: 'Environment debugging is only available in development mode' }, + { status: 403 } + ); + } + + // Categorize environment variables + const envCategories = { + nextPublic: {} as Record, + firebase: {} as Record, + development: {} as Record, + system: {} as Record, + }; + + // Mask sensitive values + const maskValue = (key: string, value: string): string => { + const sensitiveKeys = ['key', 'secret', 'token', 'password']; + const isSensitive = sensitiveKeys.some(k => key.toLowerCase().includes(k)); + + if (isSensitive && value.length > 8) { + return `${value.substring(0, 4)}****${value.substring(value.length - 4)}`; + } + return value; + }; + + // Categorize all environment variables + for (const [key, value] of Object.entries(process.env)) { + if (!value) continue; + + const maskedValue = maskValue(key, value); + + if (key.startsWith('NEXT_PUBLIC_')) { + envCategories.nextPublic[key] = maskedValue; + } else if (key.includes('FIREBASE') || key.includes('FIRE_')) { + envCategories.firebase[key] = maskedValue; + } else if (['NODE_ENV', 'PORT', 'HOSTNAME'].includes(key)) { + envCategories.development[key] = maskedValue; + } else if (['PWD', 'PATH', 'HOME', 'USER'].includes(key)) { + envCategories.system[key] = maskedValue; + } + } + + const response = { + timestamp: new Date().toISOString(), + nodeEnv: process.env.NODE_ENV, + categories: envCategories, + totals: { + nextPublic: Object.keys(envCategories.nextPublic).length, + firebase: Object.keys(envCategories.firebase).length, + development: Object.keys(envCategories.development).length, + system: Object.keys(envCategories.system).length, + }, + }; + + // Also log to server console for immediate debugging + console.log('\nšŸ” Environment Debug API called:', new Date().toISOString()); + console.log('šŸ“Š Environment variable counts:', response.totals); + + return NextResponse.json(response); +} +``` + +**Usage:** +1. Start your Next.js dev server: `pnpm run dev` +2. Visit: `http://localhost:3000/api/debug/env` +3. View JSON response with categorized environment variables + +**Pros:** +- Comprehensive server environment overview +- Categorized and organized display +- Sensitive value masking built-in +- Works from browser or curl +- Includes server console logging + +**Cons:** +- Requires creating API route file +- Only shows server-side environment +- JSON format may be hard to read + +--- + +## 3. Next.js Middleware Debugging + +### 3.1 Request-Based Environment Logging + +**Best for:** Debugging environment variables for specific requests or routes + +**Implementation:** +```typescript +// middleware.ts +import { NextRequest, NextResponse } from 'next/server'; + +export function middleware(request: NextRequest) { + // Only run debug logging in development mode + if (process.env.NODE_ENV === 'development') { + // Simple environment debugging in middleware + const debugHeaders = { + 'x-debug-node-env': process.env.NODE_ENV || 'unknown', + 'x-debug-timestamp': new Date().toISOString(), + }; + + // Add debug headers to response (visible in Network tab) + const response = NextResponse.next(); + Object.entries(debugHeaders).forEach(([key, value]) => { + response.headers.set(key, value); + }); + + // Log environment info for specific paths + if (request.nextUrl.pathname.startsWith('/api/debug')) { + console.log('šŸ”§ Middleware - Environment Debug Request:', { + path: request.nextUrl.pathname, + method: request.method, + timestamp: new Date().toISOString(), + nodeEnv: process.env.NODE_ENV, + hasFirebaseConfig: !!(process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID), + }); + } + + return response; + } + + return NextResponse.next(); +} + +export const config = { + // Only run middleware on API routes and specific debug paths + matcher: ['/api/debug/:path*', '/((?!_next/static|_next/image|favicon.ico).*)'] +}; +``` + +**Usage:** +1. Access any route that matches the middleware config +2. Check server console for log output +3. Inspect Network tab headers for debug information + +**Pros:** +- Runs on every matching request +- Can add debug headers visible in browser +- Useful for route-specific debugging +- Low performance impact + +**Cons:** +- Requires middleware configuration +- Limited to server-side variables +- More complex setup than simple console.log + +--- + +## 4. React Components/Hooks for Client-Side Debugging + +### 4.1 Simple Environment Debug Hook + +**Best for:** Interactive client-side environment variable debugging + +**Implementation:** +```typescript +// hooks/useEnvDebug.ts +'use client'; + +import { useEffect, useState } from 'react'; + +interface EnvDebugInfo { + timestamp: string; + nodeEnv: string; + clientVars: Record; + runtimeInfo: { + userAgent: string; + currentUrl: string; + isClient: boolean; + }; +} + +export function useEnvDebug(enabled = false) { + const [debugInfo, setDebugInfo] = useState(null); + const [isLogging, setIsLogging] = useState(false); + + useEffect(() => { + if (!enabled || process.env.NODE_ENV !== 'development') { + return; + } + + const collectEnvInfo = (): EnvDebugInfo => { + // Collect NEXT_PUBLIC_ environment variables + const clientVars: Record = {}; + + for (const key in process.env) { + if (key.startsWith('NEXT_PUBLIC_')) { + clientVars[key] = process.env[key] || ''; + } + } + + return { + timestamp: new Date().toISOString(), + nodeEnv: process.env.NODE_ENV || 'unknown', + clientVars, + runtimeInfo: { + userAgent: typeof window !== 'undefined' ? window.navigator.userAgent : 'SSR', + currentUrl: typeof window !== 'undefined' ? window.location.href : 'SSR', + isClient: typeof window !== 'undefined', + }, + }; + }; + + const info = collectEnvInfo(); + setDebugInfo(info); + }, [enabled]); + + const logToConsole = () => { + if (!debugInfo) return; + + setIsLogging(true); + + console.group('šŸ” useEnvDebug Hook - Client Environment'); + console.log('Timestamp:', debugInfo.timestamp); + console.log('Node Environment:', debugInfo.nodeEnv); + console.log('Is Client Side:', debugInfo.runtimeInfo.isClient); + console.log('Environment Variables Count:', Object.keys(debugInfo.clientVars).length); + + console.group('šŸ“‹ Client Environment Variables:'); + Object.entries(debugInfo.clientVars).forEach(([key, value]) => { + console.log(`${key}:`, value); + }); + console.groupEnd(); + + console.log('Runtime Info:', debugInfo.runtimeInfo); + console.groupEnd(); + + setTimeout(() => setIsLogging(false), 500); + }; + + return { + debugInfo, + logToConsole, + isLogging, + hasEnvironmentVars: debugInfo ? Object.keys(debugInfo.clientVars).length > 0 : false, + }; +} +``` + +### 4.2 Floating Debug Component + +**Implementation:** +```tsx +// components/debug/SimpleEnvDebugger.tsx +'use client'; + +import { useState } from 'react'; +import { useEnvDebug } from '@/hooks/useEnvDebug'; + +interface SimpleEnvDebuggerProps { + showButton?: boolean; + autoLog?: boolean; +} + +export function SimpleEnvDebugger({ + showButton = true, + autoLog = false +}: SimpleEnvDebuggerProps) { + const [isEnabled, setIsEnabled] = useState(autoLog); + const { debugInfo, logToConsole, isLogging, hasEnvironmentVars } = useEnvDebug(isEnabled); + + // Don't render anything in production + if (process.env.NODE_ENV !== 'development') { + return null; + } + + const handleToggleDebugging = () => { + const newEnabled = !isEnabled; + setIsEnabled(newEnabled); + + if (newEnabled) { + // Small delay to ensure state is updated + setTimeout(logToConsole, 100); + } + }; + + if (!showButton && !autoLog) { + return null; + } + + return ( +
+ {showButton && ( + + )} + + {isEnabled && debugInfo && ( +
+
Node: {debugInfo.nodeEnv}
+
Vars: {Object.keys(debugInfo.clientVars).length}
+
Client: {debugInfo.runtimeInfo.isClient ? 'āœ…' : 'āŒ'}
+ {!hasEnvironmentVars && ( +
āš ļø No NEXT_PUBLIC_ vars found
+ )} +
+ )} +
+ ); +} +``` + +**Usage:** +```tsx +// Add to your app layout or any page +import { SimpleEnvDebugger } from '@/components/debug/SimpleEnvDebugger'; + +export default function Layout({ children }) { + return ( + + + {children} + + + + ); +} +``` + +**Pros:** +- Visual interface in browser +- Real-time environment monitoring +- Interactive debugging control +- Automatically hidden in production +- Shows client-side environment variables + +**Cons:** +- Requires React component integration +- Only shows NEXT_PUBLIC_ variables +- Client-side only + +--- + +## 5. Browser Dev Tools Console Approaches + +### 5.1 Console Helper Functions + +**Best for:** Manual debugging and ad-hoc testing + +**Implementation:** +```typescript +// lib/debug/consoleHelpers.ts +export function debugEnv() { + console.group('šŸ” Environment Variables Debug'); + + // Client-side environment variables + const clientVars: Record = {}; + for (const key in process.env) { + if (key.startsWith('NEXT_PUBLIC_')) { + clientVars[key] = process.env[key] || ''; + } + } + + console.log('šŸ“Š Environment Variable Count:', Object.keys(clientVars).length); + console.log('šŸŒ Node Environment:', process.env.NODE_ENV); + console.log('ā° Timestamp:', new Date().toISOString()); + + if (Object.keys(clientVars).length > 0) { + console.table(clientVars); + } else { + console.warn('āš ļø No NEXT_PUBLIC_ environment variables found'); + } + + console.groupEnd(); +} + +export function debugRuntime() { + console.group('šŸ”§ Runtime Debug Info'); + + const runtimeInfo = { + userAgent: navigator.userAgent, + currentUrl: window.location.href, + timestamp: new Date().toISOString(), + viewportSize: `${window.innerWidth}x${window.innerHeight}`, + online: navigator.onLine, + language: navigator.language, + platform: navigator.platform, + }; + + console.table(runtimeInfo); + console.groupEnd(); +} + +export async function debugServerEnv() { + try { + console.group('šŸ–„ļø Server Environment Debug'); + console.log('Fetching server environment info...'); + + const response = await fetch('/api/debug/env'); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const data = await response.json(); + + console.log('šŸ“Š Server Environment Summary:', data.totals); + console.log('šŸ• Server Timestamp:', data.timestamp); + + // Log each category + Object.entries(data.categories).forEach(([category, vars]) => { + if (Object.keys(vars as Record).length > 0) { + console.group(`${category} (${Object.keys(vars as Record).length} vars)`); + console.table(vars); + console.groupEnd(); + } + }); + + console.groupEnd(); + } catch (error) { + console.error('āŒ Failed to fetch server environment:', error); + } +} + +export function initConsoleHelpers() { + if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') { + // Add debug functions to window for easy console access + (window as any).debugEnv = debugEnv; + (window as any).debugRuntime = debugRuntime; + (window as any).debugServerEnv = debugServerEnv; + + console.log('šŸ”§ Debug helpers initialized! Try:'); + console.log(' • debugEnv() - Client environment variables'); + console.log(' • debugRuntime() - Runtime information'); + console.log(' • debugServerEnv() - Server environment via API'); + } +} +``` + +**Setup:** +```tsx +// Add to your app layout or _app.tsx +import { initConsoleHelpers } from '@/lib/debug/consoleHelpers'; + +export default function Layout({ children }) { + useEffect(() => { + initConsoleHelpers(); + }, []); + + return <>{children}; +} +``` + +**Usage:** +1. Open browser dev tools console +2. Type any of these commands: + - `debugEnv()` - Show client environment variables + - `debugRuntime()` - Show runtime info + - `debugServerEnv()` - Fetch and show server environment + +**Pros:** +- Manual control over debugging +- No UI clutter +- Comprehensive information display +- Works from browser console +- Both client and server environment access + +**Cons:** +- Requires manual console interaction +- Must remember function names +- Requires initial setup + +--- + +## 6. Minimal Configuration Debugging Methods + +### 6.1 Single Line Environment Check + +**Best for:** Quick environment validation + +```tsx +// Add this single line anywhere in your code +{process.env.NODE_ENV === 'development' && console.log('ENV_CHECK:', { NODE_ENV: process.env.NODE_ENV, FIREBASE_PROJECT: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID })} +``` + +### 6.2 Simple Environment Display Component + +```tsx +// One-liner debug component +const EnvStatus = () => process.env.NODE_ENV === 'development' ? +
+ {process.env.NODE_ENV} | {Object.keys(process.env).filter(k=>k.startsWith('NEXT_PUBLIC_')).length} vars +
: null; +``` + +--- + +## 7. Testing All Approaches + +### 7.1 Step-by-Step Testing Guide + +1. **Setup Environment Variables:** + ```bash + # Ensure you have environment variables set + echo "NEXT_PUBLIC_TEST_VAR=test_value" >> .env.development + ``` + +2. **Test Console.log Approach:** + - Add console.log to any page component + - Start dev server: `pnpm run dev` + - Check terminal output + +3. **Test API Route:** + - Create the API route file + - Visit `http://localhost:3000/api/debug/env` + - Check JSON response + +4. **Test React Hook:** + - Add the hook and component to your app + - Look for floating debug button + - Click and check browser console + +5. **Test Console Helpers:** + - Add console helpers to your app + - Open browser dev tools + - Run `debugEnv()` in console + +### 7.2 Troubleshooting Common Issues + +**Problem: No environment variables showing** +- Solution: Ensure variables start with `NEXT_PUBLIC_` +- Check `.env.development` file exists and has correct format + +**Problem: API route returns 403** +- Solution: Verify `NODE_ENV=development` +- Check API route is in correct directory structure + +**Problem: Console functions not available** +- Solution: Ensure `initConsoleHelpers()` is called +- Check browser console for initialization messages + +**Problem: Component not rendering** +- Solution: Verify component is added to layout +- Check `NODE_ENV` is set to 'development' + +--- + +## 8. Recommendations + +### 8.1 Best Practices + +1. **For Quick Debugging:** Use console.log in page components +2. **For Comprehensive Analysis:** Use API route approach +3. **For Interactive Debugging:** Use React hook with floating component +4. **For Manual Testing:** Use browser console helpers + +### 8.2 When to Use Each Approach + +- **Console.log:** When you need immediate feedback during development +- **API Route:** When you need to inspect server environment comprehensively +- **Middleware:** When you need request-specific environment debugging +- **React Hook:** When you want interactive client-side debugging +- **Console Helpers:** When you need manual control and don't want UI elements + +### 8.3 Security Considerations + +- All approaches automatically disabled in production +- Sensitive values are masked in API responses +- No environment variables are exposed beyond what Next.js already makes available +- Debug components and routes only work in development mode + +--- + +## 9. Implementation Priority + +1. **Start with:** Simple console.log approach for immediate needs +2. **Add:** API route for comprehensive server environment inspection +3. **Consider:** React hook for interactive debugging if needed +4. **Optional:** Middleware and console helpers for advanced scenarios + +All approaches are lightweight, require minimal configuration, and provide immediate feedback for environment variable debugging in Next.js applications. \ No newline at end of file