1007 lines
20 KiB
Markdown
1007 lines
20 KiB
Markdown
# 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": ["<node_internals>/**"],
|
|
"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": ["<node_internals>/**"],
|
|
"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": ["<node_internals>/**"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### 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 { <pending> }
|
|
|
|
> 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 <token>"
|
|
|
|
# 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 <token>"
|
|
|
|
# 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<AuthState>()(
|
|
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 <token>" \
|
|
-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 <div>...</div>;
|
|
}
|
|
```
|
|
|
|
## 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
|
|
```
|