API Debugging Techniques with Developer Tools

Debugging APIs is a critical skill for modern web developers. Whether you're building a new API, integrating with third-party services, or troubleshooting production issues, having the right debugging techniques can save hours of frustration.

Understanding API Debugging

API debugging involves:

  • Inspecting requests and responses: Understanding what data is being sent and received
  • Analyzing headers: Checking authentication, content types, and cache directives
  • Validating data formats: Ensuring JSON, XML, or other formats are correct
  • Monitoring performance: Identifying slow endpoints and bottlenecks
  • Tracking errors: Understanding error messages and status codes

Browser Developer Tools

Network Tab

The Network tab in browser DevTools is your first line of defense:

What to look for:

  1. Status codes: 200 (success), 404 (not found), 500 (server error)
  2. Request method: GET, POST, PUT, DELETE
  3. Response time: How long the request took
  4. Request/response headers: Authentication tokens, content type
  5. Request payload: Data being sent to the server
  6. Response body: Data returned from the server

Pro tip: Right-click any request and select "Copy as cURL" to reproduce the request in terminal or share with teammates.

Console for API Testing

Use the browser console for quick API testing:

// Simple GET request
fetch('https://api.example.com/users/123')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

// POST with JSON body
fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_TOKEN'
  },
  body: JSON.stringify({
    name: 'John Doe',
    email: 'john@example.com'
  })
})
  .then(response => response.json())
  .then(data => console.log('Success:', data))
  .catch(error => console.error('Error:', error));

Command-Line Tools

cURL

cURL is the Swiss Army knife of API testing:

# Simple GET request
curl https://api.example.com/users/123

# GET with headers
curl -H "Authorization: Bearer TOKEN" \
     https://api.example.com/users

# POST with JSON
curl -X POST https://api.example.com/users \
     -H "Content-Type: application/json" \
     -d '{"name":"John","email":"john@example.com"}'

# View response headers
curl -i https://api.example.com/users/123

# Follow redirects
curl -L https://api.example.com/redirect

# Save response to file
curl https://api.example.com/data -o output.json

HTTPie

HTTPie offers a more user-friendly syntax:

# Simple GET
http GET https://api.example.com/users/123

# POST with JSON (auto-detected)
http POST https://api.example.com/users \
     name="John Doe" \
     email="john@example.com"

# Custom headers
http GET https://api.example.com/users \
     Authorization:"Bearer TOKEN"

# Download file
http --download https://api.example.com/file.pdf

Common API Issues and Solutions

Issue 1: CORS Errors

Symptom: "Access-Control-Allow-Origin" error in browser console

Solution:

  • Ensure server sends proper CORS headers
  • Use a proxy in development
  • Check request method (preflight for PUT/DELETE)
// Server-side (Express)
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  next();
});

Issue 2: Authentication Failures

Symptom: 401 Unauthorized or 403 Forbidden

Checklist:

  • Verify token is being sent: Check Network tab → Headers
  • Check token format: Usually "Bearer TOKEN" or "Token TOKEN"
  • Ensure token hasn't expired
  • Verify token has correct permissions
// Debugging auth headers
const token = localStorage.getItem('authToken');
console.log('Token:', token);

fetch('/api/protected', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
  .then(response => {
    console.log('Status:', response.status);
    return response.json();
  });

Issue 3: Malformed JSON

Symptom: "SyntaxError: Unexpected token" or 400 Bad Request

Solution:

  • Validate JSON before sending: Use JSON.parse() to test
  • Check for trailing commas
  • Ensure proper escaping of special characters
  • Use tools like toolcli JSON Formatter to validate
// Validate JSON before sending
const data = {
  name: 'John Doe',
  email: 'john@example.com'
};

try {
  const jsonString = JSON.stringify(data);
  JSON.parse(jsonString); // Test if valid
  // Send request
} catch (error) {
  console.error('Invalid JSON:', error);
}

Issue 4: Timeout Errors

Symptom: Request takes too long or fails with timeout

Solutions:

  • Increase timeout settings
  • Check server logs for slow queries
  • Implement request cancellation
  • Add loading indicators
// Implement timeout with AbortController
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

fetch('/api/slow-endpoint', {
  signal: controller.signal
})
  .then(response => {
    clearTimeout(timeoutId);
    return response.json();
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.error('Request timeout');
    }
  });

Advanced Debugging Techniques

1. Request Interceptors

Log all requests automatically:

// Axios interceptor
axios.interceptors.request.use(request => {
  console.log('Starting Request', {
    url: request.url,
    method: request.method,
    data: request.data
  });
  return request;
});

axios.interceptors.response.use(
  response => {
    console.log('Response:', response.status, response.data);
    return response;
  },
  error => {
    console.error('Request Failed:', {
      status: error.response?.status,
      data: error.response?.data
    });
    return Promise.reject(error);
  }
);

2. Mock API Responses

Test error handling without hitting real APIs:

// Simple mock function
const mockFetch = (url, options) => {
  console.log('Mock Request:', url, options);

  return Promise.resolve({
    ok: true,
    status: 200,
    json: () => Promise.resolve({
      id: 1,
      name: 'Test User'
    })
  });
};

// Use in development
const apiFetch = "production" === 'development'
  ? mockFetch
  : fetch;

3. Logging Strategy

Implement structured logging:

const logger = {
  request: (method, url, data) => {
    console.group(`API Request: ${method} ${url}`);
    console.log('Timestamp:', new Date().toISOString());
    console.log('Data:', data);
    console.groupEnd();
  },

  response: (url, status, data) => {
    console.group(`API Response: ${status} ${url}`);
    console.log('Data:', data);
    console.groupEnd();
  },

  error: (url, error) => {
    console.group(`API Error: ${url}`);
    console.error('Error:', error);
    console.error('Stack:', error.stack);
    console.groupEnd();
  }
};

Tools for API Development

  1. Browser Extensions:

    • JSON Viewer: Pretty-print JSON responses
    • ModHeader: Modify request headers
    • Requestly: Intercept and modify requests
  2. Desktop Applications:

    • Postman: Full-featured API client
    • Insomnia: Lightweight API client
    • Paw (Mac): Native API tool
  3. Online Tools:

Best Practices

  1. Always check status codes: Don't assume 200 means success
  2. Log strategically: Too much logging is as bad as too little
  3. Use proper error handling: Catch and handle different error types
  4. Test edge cases: Empty responses, null values, large payloads
  5. Document your APIs: Good documentation prevents many debugging sessions

Conclusion

Effective API debugging requires the right tools, techniques, and mindset. By mastering browser DevTools, command-line utilities, and following best practices, you'll be able to quickly identify and fix API issues.

Remember: good debugging is about understanding the full request-response cycle, from client to server and back.

Happy debugging! 🐛🔍