tryError Documentation
Integration Guides
Learn how to integrate tryError with popular frameworks, libraries, and tools
Express.js Integration
Integrate tryError with Express.js for consistent error handling across your API routes.
1import express from 'express';
2import { tryAsync, isTryError } from '@try-error/core';
3
4const app = express();
5
6// Error handling middleware
7const handleTryError = (req: express.Request, res: express.Response, next: express.NextFunction) => {
8 return async (handler: (req: express.Request, res: express.Response) => Promise<any>) => {
9 const result = await tryAsync(() => handler(req, res));
10
11 if (isTryError(result)) {
12 console.error('Route error:', result);
13
14 // Map error types to HTTP status codes
15 const statusCode = getStatusCodeFromError(result);
16
17 res.status(statusCode).json({
18 error: {
19 message: result.message,
20 type: result.type,
21 ...(process.env.NODE_ENV === 'development' && {
22 stack: result.stack,
23 context: result.context
24 })
25 }
26 });
27 }
28 };
29};
30
31function getStatusCodeFromError(error: TryError): number {
32 switch (error.type) {
33 case 'ValidationError': return 400;
34 case 'AuthenticationError': return 401;
35 case 'AuthorizationError': return 403;
36 case 'NotFoundError': return 404;
37 case 'ConflictError': return 409;
38 case 'RateLimitError': return 429;
39 default: return 500;
40 }
41}
42
43// Usage in routes
44app.get('/users/:id', handleTryError(async (req, res) => {
45 const userId = req.params.id;
46
47 if (!userId) {
48 throw new Error('User ID is required');
49 }
50
51 const user = await fetchUser(userId);
52 res.json(user);
53}));
54
55app.post('/users', handleTryError(async (req, res) => {
56 const userData = req.body;
57
58 // Validation
59 const validationResult = validateUserData(userData);
60 if (isTryError(validationResult)) {
61 throw validationResult;
62 }
63
64 const newUser = await createUser(userData);
65 res.status(201).json(newUser);
66}));
Next.js Integration
Use tryError in Next.js API routes and server components for robust error handling.
API Routes
1// pages/api/users/[id].ts
2import { NextApiRequest, NextApiResponse } from 'next';
3import { tryAsync, isTryError } from '@try-error/core';
4
5export default async function handler(req: NextApiRequest, res: NextApiResponse) {
6 if (req.method !== 'GET') {
7 return res.status(405).json({ error: 'Method not allowed' });
8 }
9
10 const result = await tryAsync(async () => {
11 const { id } = req.query;
12
13 if (typeof id !== 'string') {
14 throw new Error('Invalid user ID');
15 }
16
17 return await fetchUser(id);
18 });
19
20 if (isTryError(result)) {
21 console.error('API Error:', result);
22
23 const statusCode = result.message.includes('Invalid') ? 400 : 500;
24 return res.status(statusCode).json({
25 error: result.message,
26 ...(process.env.NODE_ENV === 'development' && {
27 details: result.context
28 })
29 });
30 }
31
32 res.json(result);
33}
App Router (Next.js 13+)
1// app/api/users/[id]/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { tryAsync, isTryError } from '@try-error/core';
4
5export async function GET(
6 request: NextRequest,
7 { params }: { params: { id: string } }
8) {
9 const result = await tryAsync(async () => {
10 const user = await fetchUser(params.id);
11 return user;
12 });
13
14 if (isTryError(result)) {
15 return NextResponse.json(
16 { error: result.message },
17 { status: 500 }
18 );
19 }
20
21 return NextResponse.json(result);
22}
23
24// Server Component
25export default async function UserPage({ params }: { params: { id: string } }) {
26 const result = await tryAsync(() => fetchUser(params.id));
27
28 if (isTryError(result)) {
29 return (
30 <div className="error-container">
31 <h1>Error Loading User</h1>
32 <p>{result.message}</p>
33 </div>
34 );
35 }
36
37 return (
38 <div>
39 <h1>{result.name}</h1>
40 <p>{result.email}</p>
41 </div>
42 );
43}
Prisma Integration
Wrap Prisma operations with tryError for consistent database error handling.
1import { PrismaClient, Prisma } from '@prisma/client';
2import { tryAsync, isTryError, createTryError, fromThrown } from '@try-error/core';
3
4const prisma = new PrismaClient();
5
6// Helper to map Prisma errors to domain errors
7function mapPrismaError(error: unknown) {
8 if (error instanceof Prisma.PrismaClientKnownRequestError) {
9 switch (error.code) {
10 case 'P2002':
11 return createTryError('ConflictError', 'Resource already exists', {
12 field: error.meta?.target,
13 originalError: error
14 });
15 case 'P2025':
16 return createTryError('NotFoundError', 'Resource not found', {
17 originalError: error
18 });
19 case 'P2003':
20 return createTryError('ValidationError', 'Foreign key constraint failed', {
21 field: error.meta?.field_name,
22 originalError: error
23 });
24 }
25 }
26
27 // Return the original error wrapped
28 return fromThrown(error);
29}
30
31// Database service with tryError - no try-catch needed!
32export class UserService {
33 static async findById(id: string) {
34 const result = await tryAsync(() => prisma.user.findUnique({
35 where: { id },
36 include: { posts: true }
37 }));
38
39 if (isTryError(result)) {
40 return mapPrismaError(result);
41 }
42
43 if (!result) {
44 return createTryError('NotFoundError', `User with id ${id} not found`, {
45 userId: id
46 });
47 }
48
49 return result;
50 }
51
52 static async create(data: Prisma.UserCreateInput) {
53 const result = await tryAsync(() => prisma.user.create({ data }));
54
55 if (isTryError(result)) {
56 return mapPrismaError(result);
57 }
58
59 return result;
60 }
61
62 static async update(id: string, data: Prisma.UserUpdateInput) {
63 const result = await tryAsync(() => prisma.user.update({
64 where: { id },
65 data
66 }));
67
68 if (isTryError(result)) {
69 return mapPrismaError(result);
70 }
71
72 return result;
73 }
74
75 static async delete(id: string) {
76 const result = await tryAsync(() => prisma.user.delete({
77 where: { id }
78 }));
79
80 if (isTryError(result)) {
81 return mapPrismaError(result);
82 }
83
84 return { success: true };
85 }
86}
87
88// Usage
89async function handleUserRequest(userId: string) {
90 const userResult = await UserService.findById(userId);
91
92 if (isTryError(userResult)) {
93 if (userResult.type === 'NotFoundError') {
94 return { status: 404, error: 'User not found' };
95 }
96 if (userResult.type === 'ConflictError') {
97 return { status: 409, error: 'Conflict' };
98 }
99 return { status: 500, error: 'Database error' };
100 }
101
102 return { status: 200, data: userResult };
103}
Zod Integration
Combine tryError with Zod for robust input validation and error handling.
1import { z } from 'zod';
2import { trySync, isTryError, createTryError } from '@try-error/core';
3
4// Schema definitions
5const UserSchema = z.object({
6 name: z.string().min(2, 'Name must be at least 2 characters'),
7 email: z.string().email('Invalid email format'),
8 age: z.number().min(18, 'Must be at least 18 years old').optional(),
9});
10
11const CreateUserSchema = UserSchema.extend({
12 password: z.string().min(8, 'Password must be at least 8 characters'),
13});
14
15// Validation helper
16function validateWithTryError<T>(schema: z.ZodSchema<T>, data: unknown) {
17 return trySync(() => {
18 const result = schema.safeParse(data);
19
20 if (!result.success) {
21 const errors = result.error.errors.map(err => ({
22 field: err.path.join('.'),
23 message: err.message,
24 code: err.code
25 }));
26
27 throw createTryError('ValidationError', 'Validation failed', {
28 validationErrors: errors,
29 invalidData: data
30 });
31 }
32
33 return result.data;
34 });
35}
36
37// Usage in API routes
38export async function createUser(userData: unknown) {
39 // Validate input
40 const validationResult = validateWithTryError(CreateUserSchema, userData);
41
42 if (isTryError(validationResult)) {
43 return validationResult; // Return validation error
44 }
45
46 // Proceed with validated data
47 const result = await tryAsync(async () => {
48 const hashedPassword = await hashPassword(validationResult.password);
49
50 return await UserService.create({
51 name: validationResult.name,
52 email: validationResult.email,
53 age: validationResult.age,
54 password: hashedPassword
55 });
56 });
57
58 return result;
59}
60
61// Express middleware for validation
62function validateBody<T>(schema: z.ZodSchema<T>) {
63 return (req: express.Request, res: express.Response, next: express.NextFunction) => {
64 const validationResult = validateWithTryError(schema, req.body);
65
66 if (isTryError(validationResult)) {
67 return res.status(400).json({
68 error: 'Validation failed',
69 details: validationResult.context?.validationErrors
70 });
71 }
72
73 req.body = validationResult;
74 next();
75 };
76}
Jest Testing Integration
Test your tryError implementations effectively with Jest custom matchers and utilities.
Custom Jest Matchers
1// jest-setup.ts
2import { isTryError } from '@try-error/core';
3
4declare global {
5 namespace jest {
6 interface Matchers<R> {
7 toBeTryError(): R;
8 toBeTrySuccess(): R;
9 toHaveErrorType(type: string): R;
10 toHaveErrorMessage(message: string | RegExp): R;
11 }
12 }
13}
14
15expect.extend({
16 toBeTryError(received) {
17 const pass = isTryError(received);
18
19 if (pass) {
20 return {
21 message: () => `expected ${received} not to be a TryError`,
22 pass: true,
23 };
24 } else {
25 return {
26 message: () => `expected ${received} to be a TryError`,
27 pass: false,
28 };
29 }
30 },
31
32 toBeTrySuccess(received) {
33 const pass = !isTryError(received);
34
35 if (pass) {
36 return {
37 message: () => `expected ${received} not to be a successful result`,
38 pass: true,
39 };
40 } else {
41 return {
42 message: () => `expected ${received} to be a successful result`,
43 pass: false,
44 };
45 }
46 },
47
48 toHaveErrorType(received, expectedType) {
49 if (!isTryError(received)) {
50 return {
51 message: () => `expected ${received} to be a TryError`,
52 pass: false,
53 };
54 }
55
56 const pass = received.type === expectedType;
57
58 if (pass) {
59 return {
60 message: () => `expected error type not to be ${expectedType}`,
61 pass: true,
62 };
63 } else {
64 return {
65 message: () => `expected error type ${received.type} to be ${expectedType}`,
66 pass: false,
67 };
68 }
69 },
70
71 toHaveErrorMessage(received, expectedMessage) {
72 if (!isTryError(received)) {
73 return {
74 message: () => `expected ${received} to be a TryError`,
75 pass: false,
76 };
77 }
78
79 const pass = typeof expectedMessage === 'string'
80 ? received.message === expectedMessage
81 : expectedMessage.test(received.message);
82
83 if (pass) {
84 return {
85 message: () => `expected error message not to match ${expectedMessage}`,
86 pass: true,
87 };
88 } else {
89 return {
90 message: () => `expected error message "${received.message}" to match ${expectedMessage}`,
91 pass: false,
92 };
93 }
94 },
95});
Test Examples
1// user.test.ts
2import { UserService } from './user-service';
3
4describe('UserService', () => {
5 describe('findById', () => {
6 it('should return user when found', async () => {
7 const result = await UserService.findById('existing-id');
8
9 expect(result).toBeTrySuccess();
10 expect(result).toEqual(expect.objectContaining({
11 id: 'existing-id',
12 name: expect.any(String),
13 email: expect.any(String)
14 }));
15 });
16
17 it('should return NotFoundError when user does not exist', async () => {
18 const result = await UserService.findById('non-existent-id');
19
20 expect(result).toBeTryError();
21 expect(result).toHaveErrorType('NotFoundError');
22 expect(result).toHaveErrorMessage(/User with id .* not found/);
23 });
24 });
25
26 describe('create', () => {
27 it('should create user successfully', async () => {
28 const userData = {
29 name: 'John Doe',
30 email: 'john@example.com'
31 };
32
33 const result = await UserService.create(userData);
34
35 expect(result).toBeTrySuccess();
36 expect(result).toEqual(expect.objectContaining(userData));
37 });
38
39 it('should return ConflictError for duplicate email', async () => {
40 const userData = {
41 name: 'Jane Doe',
42 email: 'existing@example.com'
43 };
44
45 const result = await UserService.create(userData);
46
47 expect(result).toBeTryError();
48 expect(result).toHaveErrorType('ConflictError');
49 expect(result).toHaveErrorMessage('User already exists');
50 });
51 });
52});
53
54// Testing validation
55describe('User validation', () => {
56 it('should validate user data successfully', () => {
57 const validData = {
58 name: 'John Doe',
59 email: 'john@example.com',
60 age: 25
61 };
62
63 const result = validateWithTryError(UserSchema, validData);
64
65 expect(result).toBeTrySuccess();
66 expect(result).toEqual(validData);
67 });
68
69 it('should return validation error for invalid data', () => {
70 const invalidData = {
71 name: 'J', // Too short
72 email: 'invalid-email',
73 age: 15 // Too young
74 };
75
76 const result = validateWithTryError(UserSchema, invalidData);
77
78 expect(result).toBeTryError();
79 expect(result).toHaveErrorType('ValidationError');
80 expect(result.context?.validationErrors).toHaveLength(3);
81 });
82});
TypeScript Configuration
Optimize your TypeScript configuration for the best tryError experience.
1{
2 "compilerOptions": {
3 "strict": true,
4 "exactOptionalPropertyTypes": true,
5 "noUncheckedIndexedAccess": true,
6 "noImplicitReturns": true,
7 "noFallthroughCasesInSwitch": true,
8 "noImplicitOverride": true,
9 "allowUnusedLabels": false,
10 "allowUnreachableCode": false,
11
12 // For better error handling
13 "useUnknownInCatchVariables": true,
14
15 // Module resolution
16 "moduleResolution": "node",
17 "esModuleInterop": true,
18 "allowSyntheticDefaultImports": true,
19
20 // Path mapping for cleaner imports
21 "baseUrl": ".",
22 "paths": {
23 "@/*": ["src/*"],
24 "@/types/*": ["src/types/*"],
25 "@/utils/*": ["src/utils/*"]
26 }
27 },
28 "include": [
29 "src/**/*",
30 "tests/**/*",
31 "jest-setup.ts"
32 ]
33}
Key Configuration Benefits
- •
strict: true
- Enables all strict type checking - •
exactOptionalPropertyTypes: true
- Better optional property handling - •
noUncheckedIndexedAccess: true
- Safer array/object access - •
useUnknownInCatchVariables: true
- Safer error handling in catch blocks
Error Monitoring Services
Integrate tryError with popular error monitoring and analytics services to track, analyze, and debug errors in production.
Sentry Integration
Sentry provides real-time error tracking and performance monitoring. Integrate it with tryError for comprehensive error insights.
1// 1. Install Sentry
2// pnpm add @sentry/nextjs
3
4// 2. Initialize Sentry (sentry.client.config.ts)
5import * as Sentry from '@sentry/nextjs';
6
7Sentry.init({
8 dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
9 environment: process.env.NODE_ENV,
10 integrations: [
11 Sentry.replayIntegration(),
12 Sentry.feedbackIntegration({ colorScheme: 'auto' }),
13 ],
14 tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
15 replaysSessionSampleRate: 0.1,
16 replaysOnErrorSampleRate: 1.0,
17});
18
19// 3. Configure tryError with Sentry
20import { configure } from '@try-error/core';
21
22configure({
23 onError: (error) => {
24 // Capture in Sentry with rich context
25 Sentry.captureException(error, {
26 tags: {
27 errorType: error.type,
28 source: error.source?.file,
29 tryError: true,
30 },
31 contexts: {
32 tryError: {
33 type: error.type,
34 message: error.message,
35 timestamp: error.timestamp,
36 source: error.source,
37 },
38 },
39 extra: {
40 context: error.context,
41 metadata: error.metadata,
42 },
43 });
44
45 // Set user context if available
46 if (error.context?.userId) {
47 Sentry.setUser({
48 id: error.context.userId,
49 email: error.context.userEmail,
50 });
51 }
52
53 return error;
54 },
55});
56
57// 4. Usage in your application
58export async function processOrder(orderId: string) {
59 // Add breadcrumb for better debugging
60 Sentry.addBreadcrumb({
61 category: 'order',
62 message: 'Processing order',
63 level: 'info',
64 data: { orderId },
65 });
66
67 const result = await tryAsync(async () => {
68 const order = await api.processOrder(orderId);
69
70 if (!order.success) {
71 throw createTryError('OrderProcessingError', order.error.message, {
72 orderId,
73 errorCode: order.error.code,
74 // Rich context for Sentry
75 orderDetails: order.details,
76 customerInfo: order.customer,
77 });
78 }
79
80 return order;
81 });
82
83 if (isTryError(result)) {
84 // Add additional Sentry context for specific errors
85 Sentry.withScope((scope) => {
86 scope.setTag('order.failed', true);
87 scope.setContext('order', {
88 id: orderId,
89 error: result.message,
90 });
91 scope.setLevel('error');
92 });
93 }
94
95 return result;
96}
Sentry Benefits with tryError
- • Automatic error grouping by TryError type
- • Rich context from tryError's structured errors
- • Source location tracking for better debugging
- • Performance monitoring with error correlation
- • Session replay on errors for debugging
Vercel Analytics Integration
Track error metrics and user impact with Vercel Analytics custom events.
1// 1. Install Vercel Analytics
2// pnpm add @vercel/analytics
3
4// 2. Setup Analytics in your layout
5import { Analytics } from '@vercel/analytics/react';
6import { track } from '@vercel/analytics';
7import { configure } from '@try-error/core';
8
9// 3. Configure tryError to track errors
10configure({
11 onError: (error) => {
12 // Track error occurrence
13 track('error_occurred', {
14 errorType: error.type,
15 errorMessage: error.message,
16 source: error.source?.file || 'unknown',
17 path: typeof window !== 'undefined' ? window.location.pathname : 'server',
18 // Don't send sensitive data
19 severity: getSeverityLevel(error),
20 category: categorizeError(error),
21 });
22
23 // Track specific error types with custom events
24 switch (error.type) {
25 case 'ValidationError':
26 track('validation_error', {
27 fields: error.context?.validationErrors?.length || 0,
28 form: error.context?.formName,
29 });
30 break;
31
32 case 'APIError':
33 track('api_error', {
34 endpoint: error.context?.endpoint,
35 statusCode: error.context?.statusCode,
36 duration: error.context?.duration,
37 });
38 break;
39
40 case 'PaymentError':
41 track('payment_error', {
42 amount: error.context?.amount,
43 currency: error.context?.currency,
44 provider: error.context?.provider,
45 });
46 break;
47 }
48
49 return error;
50 },
51});
52
53// 4. Track error recovery and user actions
54export function CheckoutForm() {
55 const handleSubmit = async (formData) => {
56 const startTime = Date.now();
57 const result = await tryAsync(() => processCheckout(formData));
58
59 if (isTryError(result)) {
60 // Track failed checkout
61 track('checkout_failed', {
62 errorType: result.type,
63 duration: Date.now() - startTime,
64 step: result.context?.step || 'unknown',
65 });
66
67 // Track user recovery actions
68 if (result.type === 'PaymentError') {
69 track('payment_retry_shown', {
70 errorCode: result.context?.errorCode,
71 });
72 }
73 } else {
74 // Track successful checkout
75 track('checkout_success', {
76 duration: Date.now() - startTime,
77 amount: formData.amount,
78 });
79 }
80
81 return result;
82 };
83}
84
85// 5. Error Analytics Dashboard
86export function RootLayout({ children }) {
87 return (
88 <html lang="en">
89 <body>
90 {children}
91 <Analytics
92 mode={process.env.NODE_ENV}
93 beforeSend={(event) => {
94 // Redact sensitive URLs
95 if (event.url.includes('/admin') ||
96 event.url.includes('/api/internal')) {
97 return null;
98 }
99 return event;
100 }}
101 />
102 </body>
103 </html>
104 );
105}
Analytics Insights with tryError
- • Error rate tracking by type and severity
- • User impact analysis with custom events
- • Recovery rate metrics for better UX
- • A/B testing error rates across features
- • Real-time error dashboards
Other Monitoring Services
tryError can integrate with any error monitoring service through its flexible configuration API.
1import { configure } from '@try-error/core';
2import { Bugsnag, LogRocket, Rollbar, DataDog } from './monitoring-services';
3
4configure({
5 onError: (error) => {
6 // Send to multiple services
7 const errorPayload = {
8 type: error.type,
9 message: error.message,
10 timestamp: error.timestamp,
11 context: error.context,
12 stack: error.stack,
13 source: error.source,
14 };
15
16 // Bugsnag
17 if (typeof Bugsnag !== 'undefined') {
18 Bugsnag.notify(error, (event) => {
19 event.addMetadata('tryError', errorPayload);
20 });
21 }
22
23 // LogRocket
24 if (typeof LogRocket !== 'undefined') {
25 LogRocket.captureException(error, {
26 tags: { errorType: error.type },
27 extra: errorPayload,
28 });
29 }
30
31 // Rollbar
32 if (typeof Rollbar !== 'undefined') {
33 Rollbar.error(error.message, error, {
34 tryError: errorPayload,
35 });
36 }
37
38 // DataDog
39 if (typeof DataDog !== 'undefined') {
40 DataDog.logger.error(error.message, {
41 error: errorPayload,
42 service: 'frontend',
43 env: process.env.NODE_ENV,
44 });
45 }
46
47 // Custom logging endpoint
48 if (process.env.NODE_ENV === 'production') {
49 fetch('/api/errors', {
50 method: 'POST',
51 headers: { 'Content-Type': 'application/json' },
52 body: JSON.stringify({
53 error: errorPayload,
54 userAgent: navigator.userAgent,
55 timestamp: new Date().toISOString(),
56 }),
57 }).catch(() => {
58 // Silently fail if logging fails
59 });
60 }
61
62 return error;
63 },
64});
Error Monitoring Best Practices
- 1. Don't log sensitive data: Always sanitize error context before sending to external services
- 2. Use sampling in production: For high-traffic apps, sample errors to control costs. See our comprehensive sampling guide
- 3. Set up alerts: Configure alerts for critical error types and error rate spikes
- 4. Track error trends: Monitor error rates over time to catch regressions early
- 5. Correlate with deployments: Link error spikes to specific deployments for faster debugging