Recipe 03 / API client

A small client. A clear response.

This recipe creates a small GET client for JSON responses. It handles HTTP failure status, limits request time, and leaves authentication and response-shape validation to explicit later steps.

Result: A reusable JSON GET functionGo to method
  1. Define the response contract

    Inspect a representative JSON response and name the fields your application needs. Do not invent a service URL or embed a credential in a sample. Save the function below as client.mjs. Use a mock response for the first run so that testing the client does not depend on a network connection or a provider account.

  2. Keep the request bounded

    The function sends a GET request with a JSON Accept header and an abort signal. It rejects unsuccessful HTTP responses before parsing the body. The timeout covers both the request and body reading, and the timer is cleared afterward. An empty or malformed JSON body rejects too, which gives the caller a failure it must handle.

  3. Check before connecting

    Use the test-batch recipe to verify valid JSON, a 503 response, and a malformed body. The injected fetch implementation makes those checks local. Before calling a real endpoint, confirm its authentication, terms, and response schema. This minimal client has no retries; adding them requires a deliberate policy for rate limits, delay, and operations that are safe to repeat.

client.mjs

export async function getJson(url, options = {}) {
  const { fetchImpl = globalThis.fetch, timeoutMs = 5000 } = options;
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
    throw new TypeError('timeoutMs must be positive');
  }
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetchImpl(url, {
      method: 'GET',
      headers: { Accept: 'application/json' },
      signal: controller.signal
    });
    if (!response.ok) throw new Error('HTTP ' + response.status);
    return await response.json();
  } finally {
    clearTimeout(timer);
  }
}

The serving check

Successful JSON becomes a value; HTTP failures and invalid bodies reject instead of masquerading as usable data.

A brief for your assistant

Review this JSON client without changing it. Explain the success path, timeout behavior, HTTP failure handling, and malformed-body case. Identify what a caller must still validate.
Next at the counterTest batch