JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q42256F
  • License MIT

A TypeScript utility library for handling API requests with robust error handling

Package Exports

  • csbridge
  • csbridge/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (csbridge) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

CSBridge - Client-Server Bridge Utility

Overview

CSBridge is a TypeScript utility library that provides a comprehensive set of tools for handling API requests with robust error handling, retries, and status code management. It's designed to serve as a reliable bridge between client applications and backend services.

Features

  • Type-Safe API: Fully typed API service with TypeScript interfaces for requests and responses
  • Comprehensive Status Code Handling: Detailed mapping of HTTP status codes to human-readable messages
  • Automatic Retry Logic: Configurable exponential backoff retry strategy for transient failures
  • Error Standardization: Converts various error types to a standardized format using format-the-error
  • Content Type Detection: Automatically parses responses based on content type (JSON, text, binary)
  • Upload & Download Support: Specialized methods for file uploads and downloads
  • Timeout Handling: Built-in request timeout with abort controller
  • Authentication Support: Easily add authentication tokens to requests
  • Consistent Error Responses: Utils module provides standardized error responses with success/error patterns

Use Cases

  • Frontend Applications: Use as the primary HTTP client in React, Vue, Angular, or vanilla TypeScript applications
  • Backend Services: TypeScript Node.js services can use it for communicating with other microservices
  • Cross-Platform Apps: Works in any JavaScript environment with fetch support (browsers, Node.js with node-fetch)
  • API Integrations: Simplify third-party API integrations with standardized error handling

Installation

npm install csbridge

Basic Usage

import apiService from "csbridge";

// GET request
const getUsers = async () => {
  try {
    const response = await apiService.get("/users", {
      params: { limit: 10, offset: 0 },
    });
    return response.data;
  } catch (error) {
    console.error("Failed to fetch users:", error);
    throw error;
  }
};

// POST request with JSON body
const createUser = async (userData) => {
  try {
    const response = await apiService.post("/users", userData);
    return response.data;
  } catch (error) {
    console.error("Failed to create user:", error);
    throw error;
  }
};

// File upload
const uploadUserAvatar = async (userId, file) => {
  try {
    const response = await apiService.upload(`/users/${userId}/avatar`, file, { userId, timestamp: Date.now() });
    return response.data;
  } catch (error) {
    console.error("Failed to upload avatar:", error);
    throw error;
  }
};

Configuration

The library uses a default configuration that can be customized:

import { API_CONFIG } from "csbridge";

// Update configuration
API_CONFIG.baseUrl = "https://api.yourservice.com";
API_CONFIG.timeout = 60000; // 60 seconds
API_CONFIG.retryCount = 3;
API_CONFIG.retryDelay = 500; // 500ms

Advanced Features

Custom Retry Logic

import apiService from "csbridge";

const fetchWithCustomRetry = async () => {
  const response = await apiService.get("/sensitive-data", {
    retry: true,
    retryOptions: {
      retries: 5,
      delay: 1000,
      retriableStatuses: [408, 429, 500, 502, 503, 504],
    },
  });
  return response.data;
};

File Downloads

import apiService from "csbridge";

const downloadReport = async (reportId) => {
  await apiService.download(`/reports/${reportId}/download`, `report-${reportId}.pdf`);
};

Error Handling

The library standardizes errors for consistent handling using the format-the-error library:

import apiService from "csbridge";

const fetchData = async () => {
  try {
    const response = await apiService.get("/data");
    return response.data;
  } catch (error) {
    // Standardized error with: name, message, code, status, data, timestamp
    console.error(`Error ${error.status}: ${error.message}`);

    // Handle specific error codes
    if (error.status === 401) {
      // Handle authentication error
    } else if (error.status === 404) {
      // Handle not found error
    }

    throw error;
  }
};

Enhanced Error Handling with Utils Module

For even more robust error handling, use the utility functions which provide standardized error responses:

import { fetchData } from "csbridge/utils";

const getData = async () => {
  // No try-catch needed - errors are handled and returned in a consistent format
  const response = await fetchData("/data");

  if (response.success) {
    return response.data;
  } else {
    // All errors have the same structure for consistent handling
    console.error(`${response.error?.code}: ${response.error?.message}`);

    // You can check the status code
    if (response.error?.status === 404) {
      console.log("Resource not found");
    }

    // Access additional error details if available
    if (response.error?.details) {
      console.debug("Error details:", response.error.details);
    }

    return null;
  }
};

Utility Functions

The library includes a set of higher-level utility functions in utils.ts that provide standardized responses:

import { fetchData, submitData, updateData, patchData, deleteData, uploadFiles, downloadFile, createResourceApi } from "csbridge/utils";

Basic API Functions with Custom Headers

Here are examples of using each utility function with custom headers and options:

// Define a type for type safety
interface User {
  id: number;
  name: string;
  email: string;
}

// Custom headers example
const customHeaders = {
  "X-API-Key": "your-api-key",
  "X-Tenant-ID": "tenant-123",
  "Accept-Language": "en-US",
};

// GET: Fetch data with params and custom headers
const fetchUsersWithHeaders = async () => {
  const response = await fetchData<User[]>("/users", {
    params: { limit: 20, role: "admin" },
    headers: customHeaders,
    timeout: 10000, // 10 seconds
  });

  if (response.success) {
    console.log(`Found ${response.data?.length} users`);
    return response.data;
  } else {
    console.error(`Error ${response.error?.status}: ${response.error?.message}`);
    return null;
  }
};

// POST: Create a new resource with custom headers
const createUserWithHeaders = async (userData: Partial<User>) => {
  const response = await submitData<User, Partial<User>>("/users", userData, {
    headers: customHeaders,
    retry: true,
    retryOptions: { retries: 3 },
  });

  if (response.success) {
    console.log(`User created with ID: ${response.data?.id}`);
    return response.data;
  } else {
    console.error(`Failed to create user: ${response.error?.message}`);
    return null;
  }
};

// PUT: Update a resource completely with custom headers
const updateUserWithHeaders = async (userId: number, userData: User) => {
  const response = await updateData<User, User>(`/users/${userId}`, userData, {
    headers: customHeaders,
  });

  if (response.success) {
    console.log(`User ${userId} updated successfully`);
    return response.data;
  } else {
    console.error(`Failed to update user: ${response.error?.message}`);
    return null;
  }
};

// PATCH: Update a resource partially with custom headers
const patchUserWithHeaders = async (userId: number, partialData: Partial<User>) => {
  const response = await patchData<User, Partial<User>>(`/users/${userId}`, partialData, {
    headers: customHeaders,
  });

  if (response.success) {
    console.log(`User ${userId} patched successfully`);
    return response.data;
  } else {
    console.error(`Failed to patch user: ${response.error?.message}`);
    return null;
  }
};

// DELETE: Remove a resource with custom headers
const deleteUserWithHeaders = async (userId: number) => {
  const response = await deleteData(`/users/${userId}`, {
    headers: customHeaders,
  });

  if (response.success) {
    console.log(`User ${userId} deleted successfully`);
    return true;
  } else {
    console.error(`Failed to delete user: ${response.error?.message}`);
    return false;
  }
};

// UPLOAD: Upload files with custom headers
const uploadUserAvatarWithHeaders = async (userId: number, file: File) => {
  const response = await uploadFiles<{ avatarUrl: string }>(
    `/users/${userId}/avatar`,
    file,
    // Optional metadata
    { userId, timestamp: Date.now() },
    // Request options
    { headers: customHeaders }
  );

  if (response.success) {
    console.log(`Avatar uploaded successfully: ${response.data?.avatarUrl}`);
    return response.data?.avatarUrl;
  } else {
    console.error(`Failed to upload avatar: ${response.error?.message}`);
    return null;
  }
};

// DOWNLOAD: Download files with custom headers
const downloadReportWithHeaders = async (reportId: number) => {
  const response = await downloadFile(`/reports/${reportId}`, `report-${reportId}.pdf`, { headers: customHeaders });

  if (response.success) {
    console.log(`Report downloaded successfully`);
    return true;
  } else {
    console.error(`Failed to download report: ${response.error?.message}`);
    return false;
  }
};

Resource API Factory with Custom Headers

You can also use the resource API factory with custom headers and options:

// Define your resource type for type safety
interface UserType {
  id: number;
  name: string;
  email: string;
  role: string;
}

// Create a typed API for the users resource
const usersApi = createResourceApi<UserType>("/users");

// Define default options with headers to use across multiple calls
const defaultOptions = {
  headers: {
    "X-API-Key": "your-api-key",
    "X-Tenant-ID": "tenant-123",
  },
};

// Then use these strongly-typed CRUD operations with headers
const createNewUser = async (userData) => {
  const response = await usersApi.create(userData, defaultOptions);
  return response.success ? response.data : null;
};

const getUsers = async () => {
  // Get a list of users with pagination and headers
  const response = await usersApi.list({ limit: 20, offset: 0 }, defaultOptions);
  return response.success ? response.data : [];
};

const getUser = async (userId) => {
  // Get a specific user by ID with headers
  const response = await usersApi.getById(userId, defaultOptions);
  return response.success ? response.data : null;
};

const updateUser = async (userId, userData) => {
  // Use update for a full resource replacement with headers
  const response = await usersApi.update(userId, userData, defaultOptions);
  return response.success ? response.data : null;
};

const patchUser = async (userId, partialData) => {
  // Use patch for partial updates with headers
  const response = await usersApi.patch(userId, partialData, defaultOptions);
  return response.success ? response.data : null;
};

const removeUser = async (userId) => {
  // Delete a resource with headers
  const response = await usersApi.remove(userId, defaultOptions);
  return response.success;
};

Resource API Factory

The createResourceApi function creates a complete set of CRUD operations for a specific resource type, making it easy to work with REST APIs in a type-safe way:

// Define your resource type for type safety
interface UserType {
  id: number;
  name: string;
  email: string;
  role: string;
}

// Create a typed API for the users resource
const usersApi = createResourceApi<UserType>("/users");

This factory provides these methods:

Method Description Example Usage
list(params?, options?) Get all resources with optional filtering usersApi.list({ limit: 20, offset: 0 }, { headers })
getById(id, options?) Get a specific resource by ID usersApi.getById(123, { headers })
create(data, options?) Create a new resource usersApi.create({ name: "John" }, { headers })
update(id, data, options?) Replace a resource completely usersApi.update(123, userObject, { headers })
patch(id, data, options?) Update a resource partially usersApi.patch(123, { name: "Updated Name" }, { headers })
remove(id, options?) Delete a resource usersApi.remove(123, { headers })

All methods accept optional API request options, including custom headers, timeout settings, and retry options. They return responses in the standardized format with proper typing.

API Request Options

All utility functions accept an options object that can include:

interface ApiRequestOptions {
  headers?: Record<string, string>; // Custom HTTP headers
  params?: Record<string, any>; // URL query parameters
  timeout?: number; // Request timeout in milliseconds
  retry?: boolean; // Enable/disable retry logic
  retryOptions?: {
    retries?: number; // Maximum number of retries (default: 3)
    delay?: number; // Initial delay in milliseconds (default: 500)
    retriableStatuses?: number[]; // HTTP status codes to retry on
  };
}

Standardized Response Format

All utility functions return a consistent response format:

interface StandardResponse<T> {
  success: boolean; // Indicates if the request was successful
  data: T | null; // The response data (when success is true)
  error: {
    // Error details (when success is false)
    message: string; // Human-readable error message
    code?: string; // Error code for programmatic handling
    status?: number; // HTTP status code
    details?: unknown; // Additional error details like stack trace
  } | null;
  meta?: {
    // Optional metadata
    timestamp: string; // Request timestamp
    requestId?: string; // Request ID for tracing
    pagination?: {
      // Pagination details if applicable
      page: number;
      pageSize: number;
      totalItems: number;
      totalPages: number;
    };
  };
}

License

MIT