Manual Error Capturing
Learn how to manually capture errors and exceptions in both client and server code using Makerkit monitoring hooks and services.
While Makerkit automatically captures unhandled errors, you often need to capture errors manually in try-catch blocks, form submissions, or API calls. This guide shows you how to capture errors programmatically.
Error Capturing Guide
Manually capture errors in your application
Client-Side Error Capturing
Using the useCaptureException Hook
The simplest way to capture errors in React components is the useCaptureException hook:
components/error-boundary.tsx
'use client';import { useCaptureException } from '@kit/monitoring/hooks';export default function ErrorPage({ error, reset,}: { error: Error & { digest?: string }; reset: () => void;}) { // Automatically captures the error when the component mounts useCaptureException(error); return ( <div> <h2>Something went wrong</h2> <button onClick={reset}>Try again</button> </div> );}This hook captures the error once when the component mounts. It's ideal for error boundary pages.
Using the useMonitoring Hook
For more control, use the useMonitoring hook to access the monitoring service directly:
components/form.tsx
'use client';import { useMonitoring } from '@kit/monitoring/hooks';export function ContactForm() { const monitoring = useMonitoring(); const handleSubmit = async (formData: FormData) => { try { await submitForm(formData); } catch (error) { // Capture the error with context monitoring.captureException(error as Error, { formData: Object.fromEntries(formData), action: 'contact_form_submit', }); // Show user-friendly error toast.error('Failed to submit form'); } }; return ( <form action={handleSubmit}> {/* form fields */} </form> );}Capturing Events
Track custom monitoring events (not errors) using captureEvent:
Example: Tracking important actions
'use client';import { useMonitoring } from '@kit/monitoring/hooks';export function DangerZone() { const monitoring = useMonitoring(); const handleDeleteAccount = async () => { // Track the action before attempting monitoring.captureEvent('account_deletion_attempted', { userId: user.id, timestamp: new Date().toISOString(), }); try { await deleteAccount(); } catch (error) { monitoring.captureException(error as Error); } }; return ( <button onClick={handleDeleteAccount}> Delete Account </button> );}Server-Side Error Capturing
In Server Actions
lib/actions/create-project.ts
'use server';import { getServerMonitoringService } from '@kit/monitoring/server';export async function createProject(formData: FormData) { const monitoring = await getServerMonitoringService(); await monitoring.ready(); try { const project = await db.project.create({ data: { name: formData.get('name') as string, }, }); return { success: true, project }; } catch (error) { await monitoring.captureException(error as Error, { action: 'createProject', formData: Object.fromEntries(formData), }); return { success: false, error: 'Failed to create project' }; }}In API Routes
app/api/webhook/route.ts
import { getServerMonitoringService } from '@kit/monitoring/server';export async function POST(request: Request) { const monitoring = await getServerMonitoringService(); await monitoring.ready(); try { const data = await request.json(); await processWebhook(data); return Response.json({ received: true }); } catch (error) { await monitoring.captureException( error as Error, { webhook: 'stripe' }, { path: request.url, method: 'POST', } ); return Response.json( { error: 'Webhook processing failed' }, { status: 500 } ); }}Adding Context to Errors
The captureException method accepts two optional parameters for adding context:
captureException( error: Error, extra?: Record<string, unknown>, // Additional data config?: Record<string, unknown> // Provider-specific config)Extra Data
Add any relevant information that helps debug the error:
Example: Rich error context
monitoring.captureException(error, { // User context userId: user.id, userEmail: user.email, userPlan: user.subscription.plan, // Action context action: 'checkout', productId: product.id, quantity: cart.items.length, // Environment context feature_flags: getEnabledFlags(), app_version: process.env.APP_VERSION,});Configuration
Pass provider-specific configuration:
Example: Sentry-specific config
monitoring.captureException( error, { userId: user.id }, { // Sentry-specific options level: 'error', tags: { component: 'checkout', flow: 'payment', }, });Identifying Users
Associate errors with users to track issues per user:
Example: Identify user after login
'use client';import { useMonitoring } from '@kit/monitoring/hooks';export function useIdentifyUser(user: { id: string; email: string }) { const monitoring = useMonitoring(); useEffect(() => { if (user) { monitoring.identifyUser({ id: user.id, email: user.email, }); } }, [user, monitoring]);}Once identified, all subsequent errors from that session are associated with the user in your monitoring dashboard.
Best Practices
Capture at Boundaries
Capture errors at the boundaries of your application:
- Form submissions
- API calls
- Third-party integrations
- File uploads
- Payment processing
Pattern: Error boundary function
async function withErrorCapture<T>( fn: () => Promise<T>, context: Record<string, unknown>): Promise<T | null> { const monitoring = await getServerMonitoringService(); await monitoring.ready(); try { return await fn(); } catch (error) { await monitoring.captureException(error as Error, context); return null; }}// Usageconst result = await withErrorCapture( () => processPayment(paymentData), { action: 'processPayment', amount: paymentData.amount });Don't Over-Capture
Not every error needs to be captured:
Example: Selective capturing
try { await fetchUserData();} catch (error) { if (error instanceof NetworkError) { // Expected error, handle gracefully return fallbackData; } if (error instanceof AuthError) { // Expected error, redirect to login redirect('/login'); } // Unexpected error, capture it monitoring.captureException(error as Error); throw error;}Include Actionable Context
Add context that helps you fix the issue:
Good context
monitoring.captureException(error, { userId: user.id, action: 'export_csv', rowCount: data.length, filters: appliedFilters, exportFormat: 'csv',});Less useful context
monitoring.captureException(error, { error: 'something went wrong', time: Date.now(),});