Error Boundaries concept
In this page:
What Is an Error Boundary?
An error boundary is a defined point in an application where a failure gets caught, logged, and converted into a controlled result instead of crashing or propagating unpredictably further up the call stack.
Example: What Is an Error Boundary?
function boundary<T>(fn: () => T, fallback: T): T {
try {
return fn();
} catch {
return fallback;
}
}
console.log(boundary(() => { throw new Error("fail"); }, "default value"));
Boundaries Around Services
A boundary placed around a service call can prevent low-level failures — a database timeout, a malformed API response — from leaking raw implementation details into higher-level application code that shouldn't need to know about them.
Example: Boundaries Around Services
function callService(): string {
throw new Error("Database timeout");
}
function boundary<T>(fn: () => T, fallback: T): T {
try {
return fn();
} catch {
return fallback;
}
}
console.log(boundary(callService, "service unavailable"));
Boundaries in HTTP Applications
Web applications commonly place an error boundary around request handling so any failure during a request becomes a controlled HTTP response (like a 500 with a safe message) instead of an unhandled crash.
Example: Boundaries in HTTP Applications
function handleRequest(): { status: number; body: string } {
try {
throw new Error("unexpected failure");
} catch {
return { status: 500, body: "Internal Server Error" };
}
}
console.log(handleRequest());
Boundaries and Logging
A good error boundary should record enough detail internally for debugging — stack trace, request context — while returning a much safer, less detailed message to the end user or external caller.
Example: Boundaries and Logging
function handleRequest() {
try {
throw new Error("db connection lost");
} catch (err) {
console.log("[internal log]", err); // detailed, for developers
return { status: 500, body: "Something went wrong" }; // safe, for users
}
}
console.log(handleRequest());
Designing Useful Boundaries
Place boundaries at a meaningful architectural level, avoid swallowing errors so silently that real bugs go unnoticed, and aim for a boundary that returns a predictable, well-typed result either way.
Example: Designing Useful Boundaries
function boundary<T>(fn: () => T, fallback: T): T {
try {
return fn();
} catch (err) {
console.log("Caught at boundary:", err);
return fallback;
}
}
console.log(boundary(() => JSON.parse("not json"), null));
Chapter Quiz — Complete all 5 topics to unlock
0/5 topics done
Complete these topics first: