Skip to content

Debugging

Tools and techniques for debugging the 2KRIKA application effectively.

Overview

  • VS Code debugger for breakpoints
  • Logging for runtime insights
  • Database queries for data issues
  • Network inspection for API calls
  • Error tracking for production issues

VS Code Debugging

Launch Configuration

Create .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug NestJS",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["dev"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "restart": true,
      "protocol": "inspector",
      "sourceMaps": true,
      "envFile": "${workspaceFolder}/.env",
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Debug Jest Tests",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["test", "--runInBand", "--no-cache"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "protocol": "inspector",
      "env": {
        "NODE_ENV": "test"
      }
    },
    {
      "name": "Debug Current Test File",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["test", "${file}", "--runInBand"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "protocol": "inspector"
    }
  ]
}

Using Breakpoints

  1. Set breakpoint – Click left of line number
  2. Start debugging – Press F5 or Run → Start Debugging
  3. Debug controls:
  4. Continue (F5) – Resume execution
  5. Step Over (F10) – Execute current line
  6. Step Into (F11) – Enter function call
  7. Step Out (Shift+F11) – Exit current function
  8. Restart (Ctrl+Shift+F5) – Restart debugger

Debug Console

Access variables and execute code:

// In debug console
user.email
order.calculateTotal()
await orderRepository.get('order-123')

Conditional Breakpoints

Right-click breakpoint → Edit Breakpoint:

// Only break when condition is true
order.status === 'completed'
userId === 'user-123'
price.toNumber() > 10000

Logpoints

Log without stopping execution:

// Right-click → Add Logpoint
Order {order.id} status: {order.status}
User {user.email} logged in

Logging

Application Logging

import { logger } from '@/shared/logging/file';

// Log levels
logger.info('User logged in: ' + userId);
logger.warn('Low stock for service: ' + serviceId);
logger.error('Payment failed', error);
logger.debug('Processing order: ' + JSON.stringify(order));

Query Logging

Enable in development:

# .env
LOG_QUERIES=true

View queries in logs/query.log:

[2024-01-15T10:30:15.234Z] QUERY: SELECT * FROM users WHERE id = ?
Params: ["user-123"]

Request Logging

See HTTP requests in logs/combined.log:

[2024-01-15T10:30:00.000Z] INFO: POST /api/orders - 192.168.1.1
[2024-01-15T10:30:01.123Z] INFO: POST /api/orders - 201 - 1234b

Database Debugging

PostgreSQL Console

# Connect to database
psql $SQL_DATABASE_URL

# Common queries
\dt                          # List tables
\d+ users                    # Describe table
\di                          # List indexes

Query Performance

-- Explain query plan
EXPLAIN ANALYZE
SELECT * FROM services 
WHERE status = 'active' 
ORDER BY created_at DESC 
LIMIT 20;

-- Find slow queries
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

-- Active connections
SELECT * FROM pg_stat_activity;

Transaction Debugging

// Enable transaction logging
const sequelize = new Sequelize({
  logging: (sql) => {
    logger.query(sql);
    console.log('[Transaction]', sql);
  },
});

// Manual transaction inspection
try {
  const transaction = await sequelize.transaction();
  // ... operations
  await transaction.commit();
} catch (error) {
  logger.error('Transaction failed', error);
  await transaction.rollback();
  throw error;
}

Network Debugging

API Testing with HTTPie

# Test endpoints
http POST localhost:3000/api/auth/login \
  email=test@example.com \
  password=password123

# With authentication
http GET localhost:3000/api/orders \
  Authorization:"Bearer $TOKEN"

# View response headers
http -h GET localhost:3000/api/services

Postman/Insomnia

  1. Import OpenAPI – Use Swagger export
  2. Environment variables – Set base URL and tokens
  3. Collections – Organize by feature
  4. Pre-request scripts – Auto-refresh tokens

Network Tab (Browser)

  1. Open DevTools → Network
  2. Make API request
  3. Inspect:
  4. Request headers
  5. Request payload
  6. Response status
  7. Response body
  8. Timing

Error Debugging

Stack Traces

Read from bottom to top:

Error: Order not found
    at OrderRepository.get (order.repository.ts:45)     ← Where error was thrown
    at acceptOrder (order.usecases.ts:120)              ← Caller
    at OrderController.accept (order.controller.ts:78)  ← HTTP handler

Error Logs

Check logs/error.log:

[2024-01-15T10:32:10.789Z] ERROR: Payment failed for order: ord-456
Stack: Error: Insufficient funds
    at processPayment (payment.ts:45)
    at OrderWorkflow.handlePayment (workflow.ts:120)

Custom Error Classes

Add context to errors:

export class OrderNotFoundError extends Error {
  constructor(public orderId: string) {
    super(`Order not found: ${orderId}`);
    this.name = 'OrderNotFoundError';
  }
}

// Usage
try {
  const order = await orderRepository.get(orderId);
} catch (error) {
  if (error instanceof OrderNotFoundError) {
    logger.error(`Order ${error.orderId} not found`);
  }
  throw error;
}

Common Issues

Port Already in Use

# Find process using port 3000
lsof -i :3000

# Kill process
kill -9 <PID>

# Or use different port
PORT=3001 pnpm dev

Database Connection Issues

# Test connection
psql $SQL_DATABASE_URL

# Check environment variable
echo $SQL_DATABASE_URL

# Verify database exists
psql -l | grep 2krika

Redis Connection Failed

# Check Redis is running
redis-cli ping

# Start Redis
redis-server

# Or use Docker
docker run -p 6379:6379 redis:alpine

Migration Errors

# Check migration status
sequelize-cli db:migrate:status

# Rollback last migration
pnpm dev:migrate:undo

# Reset database (development only!)
sequelize-cli db:drop
sequelize-cli db:create
pnpm dev:migrate
pnpm dev:seed

Memory Leaks

# Run with Node inspector
node --inspect-brk $(which nest) start

# Chrome DevTools → Memory → Take Heap Snapshot
# Compare snapshots over time

TypeScript Errors

# Clean build
rm -rf web/dist
pnpm build

# Check for type errors
tsc --noEmit

# Clear Jest cache
pnpm test --clearCache

Performance Debugging

Timing Decorator

export function logExecutionTime(
  target: any,
  propertyName: string,
  descriptor: PropertyDescriptor
) {
  const method = descriptor.value;

  descriptor.value = async function (...args: any[]) {
    const start = Date.now();

    try {
      const result = await method.apply(this, args);
      const duration = Date.now() - start;

      logger.info(
        `${target.constructor.name}.${propertyName} took ${duration}ms`
      );

      return result;
    } catch (error) {
      const duration = Date.now() - start;
      logger.error(
        `${target.constructor.name}.${propertyName} failed after ${duration}ms`,
        error
      );
      throw error;
    }
  };

  return descriptor;
}

// Usage
export class OrderService {
  @logExecutionTime
  async createOrder(data: CreateOrderDTO) {
    // ... implementation
  }
}

Database Query Profiling

// Enable query logging with timing
const sequelize = new Sequelize({
  benchmark: true,
  logging: (sql, timing) => {
    logger.query(`${sql} [${timing}ms]`);
  },
});

Cache Hit Rate

let cacheHits = 0;
let cacheMisses = 0;

async function getCached(key: string, fetchFn: () => Promise<any>) {
  const cached = await cache.get(key);

  if (cached) {
    cacheHits++;
    logger.debug(`Cache HIT: ${key}`);
    return cached;
  }

  cacheMisses++;
  logger.debug(`Cache MISS: ${key}`);
  const data = await fetchFn();
  await cache.set(key, data);

  return data;
}

// Report hit rate
function reportCacheStats() {
  const total = cacheHits + cacheMisses;
  const hitRate = total > 0 ? (cacheHits / total) * 100 : 0;

  logger.info(`Cache hit rate: ${hitRate.toFixed(2)}%`);
}

Debugging Tools

NestJS DevTools

# Install
pnpm add @nestjs/devtools-integration

# Enable in app.module.ts
import { DevtoolsModule } from '@nestjs/devtools-integration';

@Module({
  imports: [
    DevtoolsModule.register({
      http: process.env.NODE_ENV !== 'production',
    }),
  ],
})

Sequelize Logging

const sequelize = new Sequelize({
  logging: console.log,           // All queries
  logging: false,                 // Disable
  logging: (sql) => logger.query(sql), // Custom logger
});

Node Inspector

# Start with inspector
node --inspect web/dist/main.js

# Chrome DevTools
chrome://inspect

# VS Code will auto-attach

Best Practices

✅ Do

  • Use debugger – Breakpoints over console.log
  • Log context – Include IDs and metadata
  • Check error logs – Review logs/error.log regularly
  • Test queries – Profile slow queries
  • Use transactions – Easier to debug data issues
  • Add error context – Custom error classes with details
  • Monitor performance – Track execution times
  • Clean up logs – Rotate and archive old logs

❌ Don't

  • Don't commit console.log – Use proper logger
  • Don't ignore warnings – They indicate issues
  • Don't debug in production – Use proper monitoring
  • Don't swallow errors – Always log and handle
  • Don't skip stack traces – They show error origin
  • Don't debug with data mutations – Use read-only queries
  • Don't forget to remove breakpoints – Clean up before commit

Debugging Checklist

When encountering issues:

  • [ ] Read error message – What does it say?
  • [ ] Check stack trace – Where did it fail?
  • [ ] Review recent changes – What changed?
  • [ ] Check environment – Correct .env values?
  • [ ] Verify database – Data as expected?
  • [ ] Test in isolation – Unit test the issue
  • [ ] Check logs – Any related errors?
  • [ ] Review documentation – Known issue?
  • [ ] Search similar issues – Stack Overflow, GitHub
  • [ ] Ask for help – Team, community

Useful Commands

# Logs
tail -f logs/combined.log     # Watch all logs
tail -f logs/error.log        # Watch errors
grep "user-123" logs/*.log    # Search logs

# Database
psql $SQL_DATABASE_URL        # Connect to DB
pg_dump $SQL_DATABASE_URL > backup.sql  # Backup

# Redis
redis-cli                     # Connect to Redis
redis-cli MONITOR             # Watch all commands

# Process
ps aux | grep node            # Find Node processes
lsof -i :3000                 # Port usage

# Network
netstat -an | grep 3000       # Check port
curl -v localhost:3000/health # Test endpoint

Summary

Effective debugging requires:

  • Proper tools – VS Code debugger, loggers
  • Systematic approach – Reproduce, isolate, fix
  • Good logging – Context and details
  • Understanding – Read errors carefully
  • Patience – Debug step by step

Master these techniques for faster problem resolution and more reliable code.