# Debugging Guide Comprehensive guide to debugging Changemaker Lite V2 applications, covering API, frontend, database, and Docker debugging techniques. ## Overview Effective debugging requires: - Understanding the tools (VSCode, Chrome DevTools, logs) - Systematic approach (reproduce, isolate, fix, verify) - Knowledge of common issues This guide covers debugging strategies for all parts of V2. ## API Debugging ### VSCode Debugging #### Launch Configuration Create `.vscode/launch.json`: ```json { "version": "0.2.0", "configurations": [ { "name": "Debug API", "type": "node", "request": "launch", "runtimeExecutable": "npm", "runtimeArgs": ["run", "dev"], "cwd": "${workspaceFolder}/api", "console": "integratedTerminal", "skipFiles": ["/**"], "envFile": "${workspaceFolder}/.env", "sourceMaps": true, "restart": true, "protocol": "inspector" }, { "name": "Debug Media API", "type": "node", "request": "launch", "runtimeExecutable": "npm", "runtimeArgs": ["run", "dev:media"], "cwd": "${workspaceFolder}/api", "console": "integratedTerminal", "skipFiles": ["/**"], "envFile": "${workspaceFolder}/.env" }, { "name": "Attach to API (Docker)", "type": "node", "request": "attach", "port": 9229, "address": "localhost", "restart": true, "sourceMaps": true, "localRoot": "${workspaceFolder}/api", "remoteRoot": "/app", "skipFiles": ["/**"] } ] } ``` #### Start Debugging 1. Open VSCode 2. Open Run and Debug panel (Cmd+Shift+D / Ctrl+Shift+D) 3. Select "Debug API" configuration 4. Press F5 to start debugging 5. API starts with debugger attached #### Set Breakpoints Click line number gutter to set breakpoint: ```typescript // api/src/modules/auth/auth.service.ts async login(email: string, password: string) { const user = await this.prisma.user.findUnique({ // ← Click here where: { email } }); if (!user) { throw new Error('User not found'); // ← Or here } // Breakpoint pauses execution const isValid = await bcrypt.compare(password, user.password); return { user, tokens: this.generateTokens(user) }; } ``` #### Debug Features **Step Controls:** - **F10:** Step over (next line) - **F11:** Step into (enter function) - **Shift+F11:** Step out (exit function) - **F5:** Continue (run to next breakpoint) **Inspect Variables:** - Hover over variable to see value - Use "Variables" panel to see all local variables - Use "Watch" panel to monitor specific expressions **Debug Console:** - Evaluate expressions while paused - Call functions with current scope ```typescript // In debug console (while paused) > user.email 'john@example.com' > bcrypt.compare('test', user.password) Promise { } > await bcrypt.compare('test', user.password) false ``` **Call Stack:** - See function call hierarchy - Click stack frame to jump to code - Useful for understanding execution flow ### Logging (Winston) #### Using Logger ```typescript import { logger } from '../../utils/logger'; // Info level logger.info('User logged in', { userId: user.id, email: user.email }); // Error level logger.error('Failed to create user', { error: error.message, stack: error.stack, email }); // Warn level logger.warn('Deprecated endpoint accessed', { endpoint: req.path }); // Debug level (only in development) logger.debug('Processing request', { method: req.method, path: req.path, query: req.query }); ``` #### Log Output **Development (console):** ``` [2026-02-13 10:30:45] INFO: User logged in {"userId":1,"email":"john@example.com"} [2026-02-13 10:30:46] ERROR: Failed to create user {"error":"Email already exists","email":"john@example.com"} ``` **Production (JSON):** ```json {"level":"info","message":"User logged in","userId":1,"email":"john@example.com","timestamp":"2026-02-13T10:30:45.123Z"} {"level":"error","message":"Failed to create user","error":"Email already exists","email":"john@example.com","timestamp":"2026-02-13T10:30:46.456Z"} ``` #### Log Levels Set log level via environment: ```bash # .env LOG_LEVEL=debug # dev: debug, info, warn, error LOG_LEVEL=info # prod: info, warn, error ``` ### Database Query Logging #### Prisma Query Logging Enable in Prisma Client: ```typescript // api/src/config/prisma.ts const prisma = new PrismaClient({ log: [ { emit: 'event', level: 'query' }, { emit: 'event', level: 'error' }, { emit: 'event', level: 'warn' } ] }); prisma.$on('query', (e) => { logger.debug('Prisma query', { query: e.query, params: e.params, duration: e.duration }); }); prisma.$on('error', (e) => { logger.error('Prisma error', { target: e.target, message: e.message }); }); ``` **Output:** ``` [2026-02-13 10:30:45] DEBUG: Prisma query { "query": "SELECT * FROM users WHERE id = $1", "params": "[1]", "duration": 5 } ``` #### Slow Query Logging Log slow queries: ```typescript prisma.$on('query', (e) => { if (e.duration > 100) { // > 100ms logger.warn('Slow query detected', { query: e.query, duration: e.duration, params: e.params }); } }); ``` ### Network Debugging #### Request Logging Log all HTTP requests: ```typescript // api/src/middleware/logger.ts import { Request, Response, NextFunction } from 'express'; import { logger } from '../utils/logger'; export function requestLogger(req: Request, res: Response, next: NextFunction) { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; logger.info('HTTP request', { method: req.method, path: req.path, status: res.statusCode, duration, ip: req.ip, userAgent: req.get('user-agent') }); if (duration > 1000) { logger.warn('Slow request', { method: req.method, path: req.path, duration }); } }); next(); } // In server.ts app.use(requestLogger); ``` #### Testing with curl ```bash # GET request curl http://localhost:4000/api/users # POST request with JSON curl -X POST http://localhost:4000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"Admin123!"}' # With authentication curl http://localhost:4000/api/users \ -H "Authorization: Bearer " # Verbose output (see headers) curl -v http://localhost:4000/api/users # Save response to file curl http://localhost:4000/api/users > users.json ``` #### Testing with HTTPie ```bash # Install httpie brew install httpie # macOS sudo apt install httpie # Linux # GET request http localhost:4000/api/users # POST request http POST localhost:4000/api/auth/login \ email=admin@example.com \ password=Admin123! # With authentication http localhost:4000/api/users \ Authorization:"Bearer " # Pretty JSON output http --pretty=all localhost:4000/api/users ``` ## Frontend Debugging ### Chrome DevTools #### Opening DevTools - **F12** or **Cmd+Option+I** (Mac) / **Ctrl+Shift+I** (Windows/Linux) #### Console Tab View console logs and errors: ```typescript // admin/src/pages/UsersPage.tsx console.log('Users loaded', users); console.error('Failed to fetch users', error); console.warn('Deprecated API used'); console.table(users); // Display as table // Conditional logging if (import.meta.env.DEV) { console.log('Debug info', { users, loading }); } ``` **Output:** ``` Users loaded [{ id: 1, email: 'john@example.com' }, ...] ``` #### Sources Tab Debug JavaScript/TypeScript: 1. Open Sources tab 2. Find file in file tree (webpack://./src/) 3. Click line number to set breakpoint 4. Interact with UI to trigger breakpoint 5. Use step controls (same as VSCode) **Conditional Breakpoints:** - Right-click line number - Select "Add conditional breakpoint" - Enter condition: `user.id === 1` - Pauses only when condition is true #### Network Tab Debug API calls: 1. Open Network tab 2. Filter by "Fetch/XHR" 3. Interact with UI 4. Click request to see: - Headers (request/response) - Payload (request body) - Preview (formatted response) - Response (raw response) - Timing (request duration) **Common Issues:** - **404 Not Found:** Check URL path - **401 Unauthorized:** Check token/auth header - **500 Server Error:** Check API logs - **CORS Error:** Check CORS_ORIGIN setting #### Application Tab Inspect storage: - **Local Storage:** See persisted auth tokens - **Session Storage:** See session data - **Cookies:** See cookies - **Cache Storage:** See cached resources ```typescript // View in console localStorage.getItem('auth-token'); sessionStorage.getItem('cart'); ``` ### React DevTools #### Installation Install browser extension: - [Chrome](https://chrome.google.com/webstore/detail/react-developer-tools) - [Firefox](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/) #### Components Tab Inspect React component tree: 1. Open DevTools 2. Go to "Components" tab 3. Select component from tree 4. View: - Props - State (hooks) - Context - Owner (parent component) **Edit Props/State:** - Click value to edit - Change takes effect immediately - Useful for testing edge cases #### Profiler Tab Profile component renders: 1. Go to "Profiler" tab 2. Click "Record" 3. Interact with UI 4. Click "Stop" 5. See: - Flame graph (render hierarchy) - Ranked chart (slowest components) - Component details (render duration) **Identify Performance Issues:** - Components rendering too often - Slow component renders - Unnecessary re-renders ### Zustand DevTools #### Enable Redux DevTools Already configured in stores: ```typescript // admin/src/stores/auth.store.ts import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; export const useAuthStore = create()( devtools( (set, get) => ({ user: null, isAuthenticated: false, setUser: (user) => set({ user, isAuthenticated: !!user }), logout: () => set({ user: null, isAuthenticated: false }) }), { name: 'AuthStore' } // Name in DevTools ) ); ``` #### Using Redux DevTools 1. Install [Redux DevTools extension](https://chrome.google.com/webstore/detail/redux-devtools) 2. Open DevTools 3. Go to "Redux" tab 4. Select store from dropdown (AuthStore, CanvassStore) 5. View: - State tree - Action history - State diff **Features:** - Time-travel debugging (jump to previous state) - Action replay - State export/import ### VSCode Debugging (Frontend) #### Launch Configuration ```json { "name": "Debug Admin (Chrome)", "type": "chrome", "request": "launch", "url": "http://localhost:3000", "webRoot": "${workspaceFolder}/admin/src", "sourceMapPathOverrides": { "webpack:///./*": "${webRoot}/*", "webpack:///src/*": "${webRoot}/*", "webpack:///*": "*" }, "userDataDir": false } ``` **Start Debugging:** 1. Start Admin dev server: `npm run dev` 2. Select "Debug Admin (Chrome)" in VSCode 3. Press F5 4. Chrome opens with debugger attached 5. Set breakpoints in VSCode 6. Breakpoints hit when code executes ## Database Debugging ### Prisma Studio Visual database browser: ```bash # Start Prisma Studio cd api npx prisma studio ``` **Features:** - Browse all tables - Filter and sort data - Edit records directly - Create new records - Delete records **Use Cases:** - Inspect database state - Manual data fixes - Verify migrations - Test queries ### PostgreSQL Shell Direct database access: ```bash # Connect to database docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db # List tables \dt # Describe table \d users # Run query SELECT * FROM users WHERE role = 'SUPER_ADMIN'; # Count records SELECT COUNT(*) FROM campaigns; # Exit \q ``` **Common Queries:** ```sql -- Find user by email SELECT * FROM users WHERE email = 'admin@example.com'; -- Count users by role SELECT role, COUNT(*) FROM users GROUP BY role; -- Recent campaigns SELECT * FROM campaigns ORDER BY created_at DESC LIMIT 10; -- Users without name SELECT * FROM users WHERE name IS NULL; -- Delete test data DELETE FROM users WHERE email LIKE '%test%'; ``` ### Query Analysis #### Explain Query Plan ```sql EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'admin@example.com'; ``` **Output:** ``` Index Scan using users_email_key on users (cost=0.28..8.29 rows=1 width=...) Index Cond: (email = 'admin@example.com'::text) Planning Time: 0.123 ms Execution Time: 0.045 ms ``` **Identify Issues:** - Sequential scans (slow on large tables) - Missing indexes - Expensive joins #### Slow Query Log Enable slow query logging: ```sql -- Set log threshold (100ms) ALTER DATABASE changemaker_v2_db SET log_min_duration_statement = 100; -- View slow queries in logs docker compose logs v2-postgres | grep "duration:" ``` ## Docker Debugging ### Container Logs View container output: ```bash # All services docker compose logs -f # Specific service docker compose logs -f api # Last 100 lines docker compose logs --tail=100 api # With timestamps docker compose logs -t -f api # Since specific time docker compose logs --since 2024-01-01T10:00:00 api ``` ### Execute Commands in Container ```bash # Shell access docker compose exec api sh # Run command docker compose exec api npm run type-check # Run as specific user docker compose exec -u root api sh # Non-interactive command docker compose exec -T api npm run lint ``` ### Inspect Container ```bash # Container details docker inspect api # Environment variables docker inspect api | grep -A 20 "Env" # Mounts docker inspect api | grep -A 50 "Mounts" # Network settings docker inspect api | grep -A 20 "Networks" # Resource limits docker inspect api | grep -A 10 "Memory" ``` ### Container Stats ```bash # Real-time stats docker stats # Specific container docker stats api # Format output docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" ``` ### Network Debugging ```bash # Test connectivity between containers docker compose exec api ping v2-postgres docker compose exec api ping redis # Check listening ports docker compose exec api netstat -tuln # Test HTTP endpoint from inside container docker compose exec api wget -O- http://localhost:4000/health # DNS lookup docker compose exec api nslookup v2-postgres ``` ## Common Issues ### 401 Unauthorized **Symptoms:** API returns 401 for authenticated requests. **Causes:** 1. Token expired 2. Invalid token 3. Missing Authorization header 4. Token format incorrect **Debug:** ```bash # Check token in browser DevTools localStorage.getItem('auth-token') # Test token with curl curl http://localhost:4000/api/users \ -H "Authorization: Bearer " \ -v # Decode JWT (jwt.io) # Check expiration (exp claim) ``` **Fix:** - Refresh token - Re-login - Check token format (Bearer prefix) ### 500 Internal Server Error **Symptoms:** API returns 500 error. **Causes:** 1. Unhandled exception 2. Database error 3. External service failure **Debug:** ```bash # Check API logs docker compose logs -f api # Look for error stack trace docker compose logs api | grep -A 20 "Error:" # Check database connection docker compose exec api npx prisma db execute --stdin <<< "SELECT 1" ``` **Fix:** - Check error message in logs - Verify database is running - Check external service (Redis, SMTP, etc.) ### CORS Errors **Symptoms:** Browser blocks request with CORS error. **Causes:** 1. Incorrect CORS_ORIGIN setting 2. Missing CORS headers 3. Preflight OPTIONS request fails **Debug:** ```bash # Check CORS_ORIGIN in .env grep CORS_ORIGIN .env # Test with curl (bypasses CORS) curl http://localhost:4000/api/users # Check preflight request curl -X OPTIONS http://localhost:4000/api/users \ -H "Origin: http://localhost:3000" \ -H "Access-Control-Request-Method: GET" \ -v ``` **Fix:** - Set `CORS_ORIGIN=http://localhost:3000` in .env - Restart API: `docker compose restart api` ### Database Connection Errors **Symptoms:** API fails to connect to database. **Causes:** 1. PostgreSQL not running 2. Incorrect DATABASE_URL 3. Network issue **Debug:** ```bash # Check PostgreSQL is running docker compose ps v2-postgres # Check DATABASE_URL docker compose exec api sh -c 'echo $DATABASE_URL' # Test connection docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db -c "SELECT 1" # Check logs docker compose logs v2-postgres ``` **Fix:** - Start PostgreSQL: `docker compose up -d v2-postgres` - Verify DATABASE_URL matches docker-compose.yml - Check password in .env ### Redis Connection Errors **Symptoms:** API fails to connect to Redis. **Causes:** 1. Redis not running 2. Incorrect REDIS_URL 3. Missing REDIS_PASSWORD **Debug:** ```bash # Check Redis is running docker compose ps redis # Test connection docker compose exec redis redis-cli -a your_password ping # Check Redis logs docker compose logs redis ``` **Fix:** - Start Redis: `docker compose up -d redis` - Set REDIS_PASSWORD in .env - Update REDIS_URL with password ### Hot Reload Not Working **Symptoms:** Code changes don't trigger reload. **Causes:** 1. Volume mount missing 2. File watcher not detecting changes 3. Build cache issue **Debug:** ```bash # Check volume mounts docker inspect api | grep -A 20 "Mounts" # Test file sync docker compose exec api ls -la /app/src # Check for .dockerignore blocking sync cat api/.dockerignore ``` **Fix:** - Verify volume mount in docker-compose.yml - Restart container: `docker compose restart api` - Clear cache: `rm -rf api/dist && docker compose restart api` ## Debug Checklist ### Systematic Debugging Approach 1. **Reproduce:** - Can you consistently reproduce the issue? - What are the exact steps? 2. **Isolate:** - Does it happen in all environments? - Is it specific to one user/data/scenario? 3. **Gather Information:** - Check logs (API, frontend, database) - Check network requests (DevTools) - Check error messages 4. **Form Hypothesis:** - What do you think is causing it? - What evidence supports this? 5. **Test Hypothesis:** - Set breakpoints - Add logging - Test specific scenario 6. **Fix:** - Make minimal change to fix issue - Don't fix multiple issues at once 7. **Verify:** - Re-test original scenario - Test related functionality - Check for side effects 8. **Prevent:** - Add tests to catch regression - Update documentation - Share learnings with team ## Performance Debugging ### API Response Time ```typescript // Measure endpoint performance app.get('/users', async (req, res) => { const start = Date.now(); const users = await prisma.user.findMany(); const duration = Date.now() - start; logger.info('Users endpoint', { duration, count: users.length }); res.json({ users }); }); ``` ### Database Query Performance ```typescript // Log slow queries prisma.$on('query', (e) => { if (e.duration > 100) { logger.warn('Slow query', { query: e.query, duration: e.duration, params: e.params }); } }); ``` ### Frontend Render Performance ```typescript // Measure component render time function UserList() { const renderStart = performance.now(); useEffect(() => { const renderTime = performance.now() - renderStart; if (renderTime > 16) { // > 1 frame (60fps) console.warn('Slow render', { component: 'UserList', renderTime }); } }); return
...
; } ``` ## Related Documentation - **Setup:** [Local Development Setup](local-setup.md) - **Docker:** [Docker Workflow](docker-workflow.md) - **Testing:** [Testing Guide](testing.md) - **Troubleshooting:** [Troubleshooting Guide](../troubleshooting.md) ## Summary You now know: - ✅ How to debug API with VSCode - ✅ How to use Winston logging effectively - ✅ How to debug frontend with Chrome DevTools - ✅ How to use React DevTools and Zustand DevTools - ✅ How to debug database with Prisma Studio and psql - ✅ How to debug Docker containers - ✅ Common issues and their solutions - ✅ Systematic debugging approach - ✅ Performance debugging techniques **Quick Start:** ```bash # API debugging cd api && npm run dev # Start with debugger # Set breakpoints in VSCode, press F5 # Frontend debugging # Open Chrome DevTools (F12) # Network tab for API calls, Console for logs # Database debugging npx prisma studio # Visual browser docker compose exec v2-postgres psql -U changemaker_v2 -d changemaker_v2_db # Logs docker compose logs -f api admin ```