Case 3: The "Production Crash" Issue

Problem Statement

Scenario: A user's application crashes (TypeError) whenever a search keyword returns zero results.

Critical Issue

Unsafe array access without defensive checks causes production crashes and poor user experience.

The Risky Code

risky.js
import * as Dotenv from "dotenv";
import { getJson } from "serpapi";

Dotenv.config();

const params = {
  engine: "google",
  api_key: process.env.SERPAPI_KEY,
  q: "ksjdhfksjdfhk_random_string", // Keyword that returns NO results
};

console.log("Running risky search...");

const json = await getJson(params);

// RISKY CODE:
// Directly accessing array index [0] without checking if the array exists.
// If 'organic_results' is undefined, this line causes a CRASH (TypeError).
console.log("Top Result:", json.organic_results[0].title);

console.log("This line will never be reached because the app crashed above.");

Output:

Running risky search... TypeError: Cannot read property 'title' of undefined [Error] CRASH! Application terminated.

The Root Cause

Three types of failures can occur:

  1. API Errors - Invalid key, rate limits, network issues
  2. Empty Results - Valid search but Google found nothing (200 OK response)
  3. Unsafe Access - Accessing array indices without checking existence first

The Solution

Implement three-level validation for robust error handling:

robust.js
import * as Dotenv from "dotenv";
import { getJson } from "serpapi";

Dotenv.config();

const params = {
  engine: "google",
  api_key: process.env.SERPAPI_KEY,
  q: "ksjdhfksjdfhk_random_string", // Intentional nonsense keyword
};

console.log("Running robust search test...");

try {
  const json = await getJson(params);

  // Validation Level 1: Check for API errors (e.g., Invalid Key, Rate Limit)
  if (json.error) {
    throw new Error(`API Returned Error: ${json.error}`);
  }

  // Validation Level 2: Check if organic data actually exists
  if (json.organic_results && json.organic_results.length > 0) {
    console.log("Top Result:", json.organic_results[0].title);
  } else {
    // Graceful handling of "Empty State" (200 OK but no results)
    console.log("Info: Request successful (200 OK), but Google found no results.");
    console.log("Suggestion: Check for typos in the keyword or location.");
  }
} catch (error) {
  // Validation Level 3: Handle Network Errors or Script Crashes
  console.error("CRITICAL ERROR:", error.message);
  console.log("Action: Logged error and escalating to dev team...");
}

Output:

Running robust search test... Info: Request successful (200 OK), but Google found no results. Suggestion: Check for typos in the keyword or location. [Handled gracefully]

Three-Level Validation Framework

Level 1: API Error Detection

if (json.error) {
  throw new Error(`API Returned Error: ${json.error}`);
}

Common API errors:

  • Invalid API key
  • Rate limit exceeded
  • Invalid parameters
  • Network timeouts

Level 2: Empty State Handling

if (json.organic_results && json.organic_results.length > 0) {
  // Process results
} else {
  // Handle empty state gracefully
}

Level 3: Exception Handling

try {
  // Your API logic
} catch (error) {
  console.error("CRITICAL ERROR:", error.message);
  // Log to monitoring service
  // Display user-friendly message
}

Key Takeaways

Best Practice

Always implement defensive checks before accessing nested properties or array indices.

  • Check json.error first to catch API-level errors
  • Validate array existence with array && array.length > 0
  • Use try-catch blocks for network and runtime errors
  • Provide helpful error messages for debugging

Production-Ready Pattern

async function safeSearch(query) {
  try {
    const json = await getJson({
      engine: "google",
      api_key: process.env.SERPAPI_KEY,
      q: query,
    });

    if (json.error) {
      return { success: false, error: json.error };
    }

    if (json.organic_results?.length > 0) {
      return { success: true, data: json.organic_results };
    }

    return { success: true, data: [], message: "No results found" };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

// Usage
const result = await safeSearch("test query");
if (result.success) {
  console.log("Results:", result.data);
} else {
  console.error("Error:", result.error);
}