npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@deliverart/sdk-js-error-handler

v2.10.2

Published

Error handling utilities for Deliverart SDK in JavaScript

Readme

@deliverart/sdk-js-error-handler

Error handling plugin for the DeliverArt JavaScript SDK. Automatically handles API errors and transforms them into typed exceptions.

Installation

npm install @deliverart/sdk-js-error-handler @deliverart/sdk-js-core
# or
pnpm add @deliverart/sdk-js-error-handler @deliverart/sdk-js-core
# or
yarn add @deliverart/sdk-js-error-handler @deliverart/sdk-js-core

Exported Types

ErrorHandlerPlugin

class ErrorHandlerPlugin implements ApiClientPlugin<{}>

Plugin that intercepts API responses and transforms error responses into typed exceptions.

Constructor:

  • constructor() - No configuration required

ApiError

class ApiError extends Error {
  readonly name = 'ApiError'
  readonly code?: string
  readonly request?: RequestInit
  readonly response?: Response
  readonly status?: number
  readonly data?: ApiErrorData
}

Main error class for API errors.

Properties:

  • name: 'ApiError' - Error name
  • code?: string - Error code from the API
  • request?: RequestInit - Original request init
  • response?: Response - HTTP response object
  • status?: number - HTTP status code
  • data?: ApiErrorData - Typed error data

ApiErrorData

type ApiErrorData =
  | {
      type: 'VALIDATION_ERROR'
      value: ValidationErrorData
    }
  | {
      type: 'UNKNOWN_ERROR'
      value: unknown
    }

Discriminated union type for different error types.

VALIDATION_ERROR - Returned when API responds with 422 status (validation failed)

  • value: ValidationErrorData - Contains validation violations

UNKNOWN_ERROR - Returned for all other error responses

  • value: unknown - Contains the raw error response

ValidationErrorData

interface ValidationErrorData {
  status: number
  violations: Array<{
    propertyPath: string
    message: string
    code: string | null
  }>
  detail: string
  type: string
  title: string
}

Structured validation error data from the API.

Properties:

  • status: number - HTTP status code (422)
  • violations: Array - List of validation violations
    • propertyPath: string - Field path that failed validation (e.g., "email", "address.city")
    • message: string - Human-readable error message
    • code: string | null - Validation error code
  • detail: string - Detailed error description
  • type: string - Error type identifier
  • title: string - Error title

FormState

type FormState<T> =
  | {
      success: true
      data: T
    }
  | {
      success: false
      error: ApiErrorData
    }

Utility type for handling form submission states (useful for React Server Actions).

FormStateError

class FormStateError extends Error {
  constructor(public readonly error: ApiErrorData)
}

Error wrapper for form state errors.

Usage

Basic Setup

import { createApiClient } from '@deliverart/sdk-js-core';
import { ErrorHandlerPlugin } from '@deliverart/sdk-js-error-handler';

const client = createApiClient({ 
  baseUrl: 'https://api.deliverart.com' 
})
  .addPlugin(new ErrorHandlerPlugin());

Handling Validation Errors

import { ApiError } from '@deliverart/sdk-js-error-handler';
import { CreateUser } from './requests';

try {
  await client.call(new CreateUser({
    email: 'invalid-email',
    name: ''
  }));
} catch (error) {
  if (error instanceof ApiError && error.data?.type === 'VALIDATION_ERROR') {
    const violations = error.data.value.violations;
    
    violations.forEach(violation => {
      console.error(`${violation.propertyPath}: ${violation.message}`);
    });
    
    // Output:
    // email: This value is not a valid email address.
    // name: This value should not be blank.
  }
}

Displaying Validation Errors in Forms

React Example

import { useState } from 'react';
import { ApiError } from '@deliverart/sdk-js-error-handler';

function CreateUserForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});
  
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setErrors({});
    
    try {
      await client.call(new CreateUser(formData));
      // Success!
    } catch (error) {
      if (error instanceof ApiError && error.data?.type === 'VALIDATION_ERROR') {
        const newErrors: Record<string, string> = {};
        
        error.data.value.violations.forEach(violation => {
          newErrors[violation.propertyPath] = violation.message;
        });
        
        setErrors(newErrors);
      }
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input name="email" />
      {errors.email && <span className="error">{errors.email}</span>}
      
      <input name="name" />
      {errors.name && <span className="error">{errors.name}</span>}
      
      <button type="submit">Create User</button>
    </form>
  );
}

Handling Different HTTP Status Codes

import { ApiError } from '@deliverart/sdk-js-error-handler';

try {
  await client.call(new GetUser('123'));
} catch (error) {
  if (error instanceof ApiError) {
    switch (error.status) {
      case 400:
        console.error('Bad Request:', error.message);
        break;
      case 401:
        console.error('Unauthorized - please login');
        window.location.href = '/login';
        break;
      case 403:
        console.error('Forbidden - insufficient permissions');
        break;
      case 404:
        console.error('User not found');
        break;
      case 422:
        // Validation error - handled above
        break;
      case 500:
        console.error('Server error');
        break;
      default:
        console.error('Unknown error:', error);
    }
  }
}

Using FormState with React Server Actions

FormState is particularly useful for Next.js Server Actions:

// app/actions/create-user.ts
'use server';

import { throwableToFormState, FormState } from '@deliverart/sdk-js-error-handler';
import { sdk } from '@/lib/sdk';
import { CreateUser } from '@deliverart/sdk-js-user';

export async function createUserAction(
  formData: FormData
): Promise<FormState<{ id: string }>> {
  return throwableToFormState(async () => {
    const result = await sdk.call(new CreateUser({
      name: formData.get('name') as string,
      email: formData.get('email') as string,
    }));
    
    return { id: result.id };
  });
}
// app/components/CreateUserForm.tsx
'use client';

import { useFormState } from 'react-dom';
import { createUserAction } from '../actions/create-user';

export function CreateUserForm() {
  const [state, formAction] = useFormState(createUserAction, null);
  
  return (
    <form action={formAction}>
      <input name="name" required />
      {state?.success === false && 
       state.error.type === 'VALIDATION_ERROR' && 
       state.error.value.violations
         .filter(v => v.propertyPath === 'name')
         .map(v => <span key={v.code} className="error">{v.message}</span>)
      }
      
      <input name="email" type="email" required />
      {state?.success === false && 
       state.error.type === 'VALIDATION_ERROR' && 
       state.error.value.violations
         .filter(v => v.propertyPath === 'email')
         .map(v => <span key={v.code} className="error">{v.message}</span>)
      }
      
      <button type="submit">Create User</button>
      
      {state?.success === true && (
        <p className="success">User created with ID: {state.data.id}</p>
      )}
    </form>
  );
}

Global Error Handler

Set up a global error handler for all API errors:

// lib/error-handler.ts
import { ApiError } from '@deliverart/sdk-js-error-handler';

export function handleApiError(error: unknown) {
  if (!(error instanceof ApiError)) {
    console.error('Non-API error:', error);
    return;
  }
  
  // Log to error tracking service (e.g., Sentry)
  console.error('API Error:', {
    status: error.status,
    code: error.code,
    message: error.message,
    data: error.data,
  });
  
  // Show user-friendly message
  if (error.status === 401) {
    alert('Please login to continue');
    window.location.href = '/login';
  } else if (error.status === 403) {
    alert('You do not have permission to perform this action');
  } else if (error.status === 404) {
    alert('The requested resource was not found');
  } else if (error.data?.type === 'VALIDATION_ERROR') {
    const firstViolation = error.data.value.violations[0];
    alert(`Validation error: ${firstViolation.message}`);
  } else {
    alert('An unexpected error occurred. Please try again.');
  }
}
// Usage in your app
try {
  await client.call(request);
} catch (error) {
  handleApiError(error);
}

Type Guards

Create type guards for cleaner error handling:

import { ApiError, ApiErrorData } from '@deliverart/sdk-js-error-handler';

function isValidationError(error: unknown): error is ApiError & { 
  data: { type: 'VALIDATION_ERROR', value: ValidationErrorData } 
} {
  return error instanceof ApiError && 
         error.data?.type === 'VALIDATION_ERROR';
}

function isUnauthorizedError(error: unknown): error is ApiError {
  return error instanceof ApiError && error.status === 401;
}

function isNotFoundError(error: unknown): error is ApiError {
  return error instanceof ApiError && error.status === 404;
}

// Usage
try {
  await client.call(request);
} catch (error) {
  if (isValidationError(error)) {
    // TypeScript knows error.data.value is ValidationErrorData
    error.data.value.violations.forEach(v => {
      console.log(v.propertyPath, v.message);
    });
  } else if (isUnauthorizedError(error)) {
    redirectToLogin();
  } else if (isNotFoundError(error)) {
    show404Page();
  }
}

Converting FormState Errors Back to Exceptions

import { formStateErrorToThrowable, FormState } from '@deliverart/sdk-js-error-handler';

const result: FormState<User> = await createUserAction(formData);

if (!result.success) {
  // Convert FormState error back to throwable error
  throw formStateErrorToThrowable(result.error);
}

// TypeScript knows result.data is User
console.log(result.data.id);

How It Works

The plugin adds a response middleware that:

  1. Checks if the response is not OK (!response.ok)
  2. If status is 422, attempts to parse the response as validation error
  3. If parsing succeeds, throws ApiError with VALIDATION_ERROR type
  4. For all other error responses, throws ApiError with UNKNOWN_ERROR type
  5. Includes the original request, response, and parsed body in the error

Best Practices

1. Centralized Error Handling

Create a utility function for consistent error handling across your app:

export function getErrorMessage(error: unknown): string {
  if (error instanceof ApiError) {
    if (error.data?.type === 'VALIDATION_ERROR') {
      const firstViolation = error.data.value.violations[0];
      return firstViolation?.message ?? 'Validation failed';
    }
    return error.message;
  }
  
  if (error instanceof Error) {
    return error.message;
  }
  
  return 'An unexpected error occurred';
}

2. Field-Level Error Mapping

Create a helper to map violations to form fields:

import { ValidationErrorData } from '@deliverart/sdk-js-error-handler';

export function violationsToFieldErrors(
  violations: ValidationErrorData['violations']
): Record<string, string> {
  return violations.reduce((acc, violation) => {
    acc[violation.propertyPath] = violation.message;
    return acc;
  }, {} as Record<string, string>);
}

// Usage
if (error.data?.type === 'VALIDATION_ERROR') {
  const fieldErrors = violationsToFieldErrors(error.data.value.violations);
  setErrors(fieldErrors);
}

3. Toast Notifications

Integrate with toast notification libraries:

import { toast } from 'sonner';
import { ApiError } from '@deliverart/sdk-js-error-handler';

try {
  await client.call(request);
  toast.success('Operation completed successfully');
} catch (error) {
  if (error instanceof ApiError && error.data?.type === 'VALIDATION_ERROR') {
    error.data.value.violations.forEach(v => {
      toast.error(v.message);
    });
  } else {
    toast.error('An error occurred');
  }
}

License

MIT