js/errors.js — typed errors
In the code column — js/errors.js. The base class AppError carries code, cause and retryable; the specific errors extend it. The point isn't the hierarchy itself — it's that the caller can branch on the error type.
Now api.js (from lesson 4) throws typed errors instead of new Error(...):
import { NetworkError, RateLimitError, NotFoundError, ApiError } from './errors.js';
async function request(url) {
let response;
try {
response = await fetch(url);
} catch (cause) {
throw new NetworkError(cause); // only a network failure lands here
}
if (response.ok) return response.json();
if (response.status === 429) throw new RateLimitError();
if (response.status === 404) throw new NotFoundError('That picture');
if (response.status === 403) { // NASA sometimes 403s a spent DEMO_KEY
const body = await response.text().catch(() => '');
if (body.toLowerCase().includes('rate limit')) throw new RateLimitError();
}
throw new ApiError(response.status, await response.text().catch(() => ''));
}
Now main.js can respond differently: if (error.retryable) offerRetry(), taking the message from toMessage(error).
Tip (where's "Sentry"?). The real
errors.jsalso hasreportError()andinstallGlobalHandlers()— one place where errors are sent to a monitoring tool (Sentry, Datadog) and where anything nobody else caught is caught (window.onerror,unhandledrejection). An app should never die silently. We'll add those parts later; for now, the point is typed errors.