Designing Error Boundaries with Claude Code: From Frontend to API
Designing error boundaries using Claude Code. From frontend to API. Includes practical code examples.
What Is an Error Boundary?
An error boundary is a defensive design pattern that prevents the entire application from crashing when part of it throws an error. With Claude Code, you can design consistent error handling across both the frontend and the backend.
React Error Boundary
import { Component, ErrorInfo, ReactNode } from "react";
interface ErrorBoundaryProps {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Error caught by boundary:", error, errorInfo);
this.props.onError?.(error, errorInfo);
// Send to error monitoring service
reportError(error, {
componentStack: errorInfo.componentStack,
});
}
render() {
if (this.state.hasError) {
return (
this.props.fallback || (
<div className="error-fallback">
<h2>Something went wrong</h2>
<p>{this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false, error: null })}>
Try again
</button>
</div>
)
);
}
return this.props.children;
}
}
Hook for Function Components
import { useCallback, useState } from "react";
function useErrorHandler() {
const [error, setError] = useState<Error | null>(null);
if (error) {
throw error; // Caught by the parent Error Boundary
}
const handleError = useCallback((err: unknown) => {
if (err instanceof Error) {
setError(err);
} else {
setError(new Error(String(err)));
}
}, []);
const withErrorHandling = useCallback(
<T,>(fn: () => Promise<T>) => {
return async () => {
try {
return await fn();
} catch (err) {
handleError(err);
return undefined;
}
};
},
[handleError]
);
return { handleError, withErrorHandling };
}
Standardizing API Error Responses
// Error code definitions
enum ErrorCode {
VALIDATION_ERROR = "VALIDATION_ERROR",
NOT_FOUND = "NOT_FOUND",
UNAUTHORIZED = "UNAUTHORIZED",
FORBIDDEN = "FORBIDDEN",
CONFLICT = "CONFLICT",
INTERNAL_ERROR = "INTERNAL_ERROR",
RATE_LIMITED = "RATE_LIMITED",
}
interface ApiError {
code: ErrorCode;
message: string;
details?: Record<string, unknown>;
requestId: string;
timestamp: string;
}
class AppError extends Error {
constructor(
public code: ErrorCode,
message: string,
public statusCode: number,
public details?: Record<string, unknown>
) {
super(message);
this.name = "AppError";
}
static notFound(resource: string) {
return new AppError(
ErrorCode.NOT_FOUND,
`${resource} not found`,
404
);
}
static validation(details: Record<string, string>) {
return new AppError(
ErrorCode.VALIDATION_ERROR,
"Validation failed",
400,
details
);
}
static unauthorized(message = "Authentication required") {
return new AppError(ErrorCode.UNAUTHORIZED, message, 401);
}
}
Global Error Handler
import { v4 as uuidv4 } from "uuid";
function globalErrorHandler(
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const requestId = uuidv4();
if (err instanceof AppError) {
// Business error
return res.status(err.statusCode).json({
code: err.code,
message: err.message,
details: err.details,
requestId,
timestamp: new Date().toISOString(),
});
}
// Unexpected error
console.error(`[${requestId}] Unexpected error:`, err);
res.status(500).json({
code: ErrorCode.INTERNAL_ERROR,
message: "An unexpected error occurred",
requestId,
timestamp: new Date().toISOString(),
});
}
// Registration order matters: place after your routers
app.use(globalErrorHandler);
Catching Async Errors
// asyncHandler wrapper
function asyncHandler(
fn: (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => Promise<void>
) {
return (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
// Usage example
router.get(
"/users/:id",
asyncHandler(async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: req.params.id },
});
if (!user) {
throw AppError.notFound("User");
}
res.json({ data: user });
})
);
Hierarchical Error Boundaries
On the React side, you place multiple error boundaries hierarchically. Use this as a reference when asking Claude Code to design error handling. For writing prompts, see 5 tips for better prompts, and for improving code quality, see automating refactoring.
function App() {
return (
<ErrorBoundary fallback={<FullPageError />}>
<Header />
<ErrorBoundary fallback={<SidebarFallback />}>
<Sidebar />
</ErrorBoundary>
<ErrorBoundary fallback={<ContentFallback />}>
<MainContent />
</ErrorBoundary>
</ErrorBoundary>
);
}
For error handling design principles, see MDN Web Docs: Error handling. For Claude Code details, see the official documentation.
Summary
Error boundaries are an important design pattern for preserving user experience. With Claude Code, you can efficiently implement consistent error handling from the frontend all the way through the API.
Level up your Claude Code workflow
50 battle-tested prompt templates you can copy-paste into Claude Code right now.
Free PDF: Claude Code Cheatsheet in 5 Minutes
Key commands, shortcuts, and prompt examples on a single printable page.
About the Author
Masa
Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.
Related Posts
Getting Started with Claude Code Agent SDK — Build Autonomous Agents Fast
Learn how to build autonomous AI agents with Claude Code Agent SDK. Covers setup, tool definitions, and multi-step execution with practical code examples.
The Complete Guide to Context Management in Claude Code
Learn practical techniques to maximize Claude Code's context window. Covers token optimization, conversation splitting, and CLAUDE.md usage.
Mastering Claude Code Hooks: Auto-Format, Auto-Test, and More
Mastering Claude Code Hooks: Auto-Format, Auto-Test, and More. A practical guide with code examples.