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

@ticatec/restful_service_api

v0.5.0

Published

A lightweight TypeScript RESTful API client for browsers with error handling. (ESM only, v0.5.0+)

Readme

@ticatec/restful_service_api

Version License: MIT

A lightweight TypeScript RESTful API client for browsers with comprehensive error handling and interceptor support.

中文版 | English

Features

  • 🚀 Lightweight: Zero dependencies, built for modern browsers
  • 🔧 TypeScript Support: Full type safety with TypeScript definitions
  • 🛡️ Error Handling: Built-in error handling with custom ApiError class
  • Interceptors: Pre and post request interceptors for authentication and data processing
  • 🎯 Flexible: Support for custom headers, timeouts, and data processors
  • 📁 File Operations: Dedicated interface for file upload/download operations
  • 🌐 Browser-First: Designed specifically for frontend applications
  • PATCH Support: Full support for HTTP PATCH method for partial updates

⚠️ Breaking Changes in v0.5.0

Starting from version 0.5.0, this package has migrated to ESM (ECMAScript Modules) format.

What This Means for You

  • Your project must use ESM format (have "type": "module" in package.json or use .mjs extension)
  • require() is no longer supported. Use import statements instead

Migration Guide

If you're upgrading from a version prior to 0.5.0:

Before (CommonJS):

const RestService = require('@ticatec/restful_service_api');

After (ESM):

import RestService from '@ticatec/restful_service_api';

For CommonJS projects, you may need to use dynamic import:

const { default: RestService } = await import('@ticatec/restful_service_api');

Installation

npm install @ticatec/restful_service_api

Quick Start

import RestService, { FileService } from '@ticatec/restful_service_api';

// Your implementation of RestService interface
class MyApiClient implements RestService {
    async get(url: string, params?: any, dataProcessor?: DataProcessor) {
        // Implementation here
    }

    async post(url: string, data?: any, options?: RestfulOptions) {
        // Implementation here
    }

    async patch(url: string, data?: any, options?: RestfulOptions) {
        // Implementation here
    }

    // ... other methods
}

// Your implementation of FileService interface
class MyFileClient implements FileService {
    async upload(url: string, params: any, file: File, fileKey?: string, dataProcessor?: DataProcessor) {
        // Implementation here
    }

    async asyncUpload(url: string, params: any, file: File, callback: UploadCallback, fileKey?: string) {
        // Implementation here
    }

    async download(url: string, filename: string, params: any, method?: string, formData?: any) {
        // Implementation here
    }
}

const api = new MyApiClient();
const fileApi = new MyFileClient();

// Make REST requests
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'John Doe' });
const partialUpdate = await api.patch('/users/1', { status: 'active' });

// File operations
const result = await fileApi.upload('/upload', { userId: 123 }, file);

API Reference

RestService Interface

The main interface for standard HTTP REST operations:

interface RestService {
    get(url: string, params?: any, dataProcessor?: DataProcessor): Promise<any>;
    post(url: string, data?: any, options?: RestfulOptions): Promise<any>;
    put(url: string, data?: any, options?: RestfulOptions): Promise<any>;
    patch(url: string, data?: any, options?: RestfulOptions): Promise<any>;
    del(url: string, data?: any, options?: RestfulOptions): Promise<any>;
}

FileService Interface

Dedicated interface for file upload and download operations:

interface FileService {
    upload(url: string, params: any, file: File, fileKey?: string, dataProcessor?: DataProcessor): Promise<any>;
    asyncUpload(url: string, params: any, file: File, callback: UploadCallback, fileKey?: string): Promise<UploadProgress>;
    download(url: string, filename: string, params: any, method?: string, formData?: any): Promise<any>;
}

RestService Methods

get(url, params?, dataProcessor?)

Performs HTTP GET request to retrieve a resource.

  • url: The endpoint URL
  • params: Query parameters (optional)
  • dataProcessor: Function to process response data (optional)

post(url, data?, options?)

Performs HTTP POST request to create a new resource.

  • url: The endpoint URL
  • data: Request payload (optional)
  • options: Optional configuration object containing:
    • params: Query parameters (optional)
    • contentType: Content-Type header (optional, defaults to application/json)
    • dataProcessor: Function to process response data (optional)

put(url, data?, options?)

Performs HTTP PUT request to update an entire resource.

  • url: The endpoint URL
  • data: Request payload (optional)
  • options: Optional configuration object containing:
    • params: Query parameters (optional)
    • contentType: Content-Type header (optional, defaults to application/json)
    • dataProcessor: Function to process response data (optional)

patch(url, data?, options?)

Performs HTTP PATCH request to partially update a resource.

  • url: The endpoint URL
  • data: Request payload with fields to update (optional)
  • options: Optional configuration object containing:
    • params: Query parameters (optional)
    • contentType: Content-Type header (optional, defaults to application/json)
    • dataProcessor: Function to process response data (optional)

del(url, data?, options?)

Performs HTTP DELETE request to delete a resource.

  • url: The endpoint URL
  • data: Request body data (optional)
  • options: Optional configuration object containing:
    • params: Query parameters (optional)
    • contentType: Content-Type header (optional, defaults to application/json)
    • dataProcessor: Function to process response data (optional)

FileService Methods

upload(url, params, file, fileKey?, dataProcessor?)

Performs synchronous file upload operation.

  • url: The upload endpoint URL
  • params: Additional parameters for the upload request
  • file: The File object to upload
  • fileKey: Optional form field name for the file (defaults to 'file')
  • dataProcessor: Function to process response data (optional)

asyncUpload(url, params, file, callback, fileKey?)

Performs asynchronous file upload with progress monitoring and cancellation support.

  • url: The upload endpoint URL
  • params: Additional parameters for the upload request
  • file: The File object to upload
  • callback: Upload callback object with progress, error, and completion handlers
  • fileKey: Optional form field name for the file (defaults to 'file')
  • Returns: Promise that resolves to UploadProgress object for cancellation

download(url, filename, params, method?, formData?)

Performs file download operation.

  • url: The download endpoint URL
  • filename: The name to save the downloaded file
  • params: Download request parameters
  • method: Optional HTTP method (defaults to GET)
  • formData: Optional form data for POST downloads

Error Handling

The library includes a custom ApiError class for handling API errors:

import { ApiError } from '@ticatec/restful_service_api';

try {
    const result = await api.get('/users');
} catch (error) {
    if (error instanceof ApiError) {
        console.log('Status:', error.status);
        console.log('Code:', error.code);
        console.log('Details:', error.details);
    }
}

Types and Interceptors

import {
    PreInterceptor,
    PostInterceptor,
    ErrorHandler,
    DataProcessor,
    RestfulOptions
} from '@ticatec/restful_service_api';

// RestfulOptions for configuring requests
const options: RestfulOptions = {
    params: { page: 1, limit: 10 },
    contentType: 'application/json',
    dataProcessor: (data: any) => data.results || data
};

// Pre-interceptor for adding authentication headers
const preInterceptor: PreInterceptor = (method: string, url: string) => {
    return {
        headers: {
            'Authorization': 'Bearer ' + getToken(),
            'X-Request-ID': generateRequestId()
        },
        timeout: 30000
    };
};

// Post-interceptor for response processing
const postInterceptor: PostInterceptor = async (data: any) => {
    // Process response data
    return data;
};

// Error handler
const errorHandler: ErrorHandler = (error: Error) => {
    console.error('API Error:', error);
    return true; // Return true if error is handled
};

// Data processor
const dataProcessor: DataProcessor = (data: any) => {
    // Transform response data
    return data.results || data;
};

Content Types

Pre-defined content type constants:

import {
    CONTENT_TYPE_NAME,
    TYPE_JSON,
    TYPE_HTML,
    TYPE_TEXT
} from '@ticatec/restful_service_api';

// Usage
await api.post('/upload', data, {}, { contentType: TYPE_JSON });

Implementation Example

Here's a complete example implementation using the native fetch API:

RestService Implementation

import RestService, {
    ApiError,
    PreInterceptor,
    PostInterceptor,
    RestfulOptions,
    TYPE_JSON,
    DataProcessor
} from '@ticatec/restful_service_api';

class FetchRestService implements RestService {
    private baseURL: string;
    private preInterceptor?: PreInterceptor;
    private postInterceptor?: PostInterceptor;

    constructor(baseURL: string, preInterceptor?: PreInterceptor, postInterceptor?: PostInterceptor) {
        this.baseURL = baseURL;
        this.preInterceptor = preInterceptor;
        this.postInterceptor = postInterceptor;
    }

    async get(url: string, params?: any, dataProcessor?: DataProcessor): Promise<any> {
        const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
        return this.request('GET', url + queryString, null, undefined, dataProcessor);
    }

    async post(url: string, data?: any, options?: RestfulOptions): Promise<any> {
        const contentType = options?.contentType || TYPE_JSON;
        const params = options?.params;
        const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
        return this.request('POST', url + queryString, data, contentType, options?.dataProcessor);
    }

    async put(url: string, data?: any, options?: RestfulOptions): Promise<any> {
        const contentType = options?.contentType || TYPE_JSON;
        const params = options?.params;
        const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
        return this.request('PUT', url + queryString, data, contentType, options?.dataProcessor);
    }

    async patch(url: string, data?: any, options?: RestfulOptions): Promise<any> {
        const contentType = options?.contentType || TYPE_JSON;
        const params = options?.params;
        const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
        return this.request('PATCH', url + queryString, data, contentType, options?.dataProcessor);
    }

    async del(url: string, data?: any, options?: RestfulOptions): Promise<any> {
        const contentType = options?.contentType || TYPE_JSON;
        const params = options?.params;
        const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
        return this.request('DELETE', url + queryString, data, contentType, options?.dataProcessor);
    }

    private async request(method: string, url: string, body?: any, contentType?: string, dataProcessor?: DataProcessor): Promise<any> {
        const fullUrl = this.baseURL + url;

        // Apply pre-interceptor
        const interceptorResult = this.preInterceptor?.(method, fullUrl) || { headers: {} };

        const headers = {
            ...interceptorResult.headers,
            ...(contentType && { 'Content-Type': contentType })
        };

        try {
            const response = await fetch(fullUrl, {
                method,
                headers,
                body: body ? JSON.stringify(body) : undefined,
                signal: interceptorResult.timeout ? AbortSignal.timeout(interceptorResult.timeout) : undefined
            });

            if (!response.ok) {
                const errorData = await response.json().catch(() => ({}));
                throw new ApiError(response.status, errorData);
            }

            let responseData = await response.json();

            // Apply post-interceptor
            if (this.postInterceptor) {
                responseData = await this.postInterceptor(responseData);
            }

            // Apply data processor
            if (dataProcessor) {
                responseData = dataProcessor(responseData);
            }

            return responseData;
        } catch (error) {
            if (error instanceof ApiError) {
                throw error;
            }
            throw new ApiError(0, { code: 'NETWORK_ERROR', message: error.message });
        }
    }
}

// Usage
const api = new FetchRestService('https://api.example.com');
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'John Doe' });
const partialUpdate = await api.patch('/users/1', { status: 'active' });
const fullUpdate = await api.put('/users/1', { name: 'Jane Doe', age: 30 });

FileService Implementation

import FileService, { UploadCallback, UploadProgress, DataProcessor } from '@ticatec/restful_service_api';

class FetchFileService implements FileService {
    private baseURL: string;

    constructor(baseURL: string) {
        this.baseURL = baseURL;
    }

    async upload(url: string, params: any, file: File, fileKey?: string, dataProcessor?: DataProcessor): Promise<any> {
        const formData = new FormData();
        formData.append(fileKey || 'file', file);

        // Add additional parameters
        Object.keys(params).forEach(key => {
            formData.append(key, params[key]);
        });

        const response = await fetch(this.baseURL + url, {
            method: 'POST',
            body: formData
        });

        if (!response.ok) {
            throw new Error(`Upload failed: ${response.statusText}`);
        }

        const data = await response.json();
        return dataProcessor ? dataProcessor(data) : data;
    }

    async asyncUpload(url: string, params: any, file: File, callback: UploadCallback, fileKey?: string): Promise<UploadProgress> {
        const formData = new FormData();
        formData.append(fileKey || 'file', file);

        Object.keys(params).forEach(key => {
            formData.append(key, params[key]);
        });

        const xhr = new XMLHttpRequest();

        return new Promise((resolve) => {
            xhr.upload.addEventListener('progress', (e) => {
                if (e.lengthComputable && callback.progressUpdate) {
                    callback.progressUpdate(e.loaded);
                }
            });

            xhr.addEventListener('load', () => {
                if (xhr.status >= 200 && xhr.status < 300) {
                    const data = JSON.parse(xhr.responseText);
                    callback.onCompleted(data);
                } else if (callback.handleError) {
                    callback.handleError(new Error(`Upload failed: ${xhr.statusText}`));
                }
            });

            xhr.addEventListener('error', () => {
                if (callback.handleError) {
                    callback.handleError(new Error('Network error'));
                }
            });

            xhr.open(callback.method || 'POST', this.baseURL + url);
            xhr.send(formData);

            resolve({
                cancel: () => xhr.abort()
            });
        });
    }

    async download(url: string, filename: string, params: any, method?: string, formData?: any): Promise<any> {
        let fullUrl = this.baseURL + url;

        if (method === 'POST' && formData) {
            const form = new FormData();
            Object.keys(formData).forEach(key => {
                form.append(key, formData[key]);
            });

            const response = await fetch(fullUrl, {
                method: 'POST',
                body: form
            });

            const blob = await response.blob();
            const downloadUrl = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = downloadUrl;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(downloadUrl);
        } else {
            const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
            fullUrl += queryString;

            const response = await fetch(fullUrl);
            const blob = await response.blob();
            const downloadUrl = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = downloadUrl;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(downloadUrl);
        }
    }
}

// Usage
const fileApi = new FetchFileService('https://api.example.com');
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = fileInput.files[0];

// Simple upload
const result = await fileApi.upload('/upload', { userId: 123 }, file);

// Async upload with progress
const progress = await fileApi.asyncUpload('/upload', { userId: 123 }, file, {
    method: 'POST',
    progressUpdate: (loaded) => console.log(`Uploaded: ${loaded} bytes`),
    onCompleted: (data) => console.log('Done!', data),
    handleError: (err) => console.error('Error!', err)
});

// Download file
await fileApi.download('/files/document.pdf', 'my-document.pdf', { userId: 123 });

Utility Functions

The library includes utility functions for common operations:

import utils from '@ticatec/restful_service_api/utils';

// Convert object to query string
const queryString = utils.toQueryString({ name: 'John', age: 30 });
// Returns: "name=John&age=30"

// Combine URL with parameters
const fullUrl = utils.combineUrl('/api/users', { page: 1, limit: 10 });
// Returns: "/api/users?page=1&limit=10"

// Generate HTTP request options
const options = utils.generateRequestOptions('POST', { id: 1 }, { name: 'John' });
// Returns: { method: 'POST', headers: {}, params: { id: 1 }, data: { name: 'John' } }

// Clean parameters (removes null, undefined, empty strings, trims values)
const cleanedParams = utils.cleanParams({ name: ' John ', age: null, email: '' });
// Returns: { name: 'John' }

// Function utilities
utils.invokeFunction(callback, arg1, arg2); // Safely invoke function if it exists
const isFunc = utils.isFunction(someValue); // Check if value is a function

Available Utility Functions

  • toQueryString(obj): Converts an object to URL query string
  • combineUrl(url, params): Combines URL with query parameters
  • generateRequestOptions(method, params, data): Generates HTTP request options
  • cleanParams(params): Cleans parameters by removing null/empty values and trimming strings
  • isFunction(value): Checks if a value is a function
  • invokeFunction(func, ...args): Safely invokes a function if it exists

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see the LICENSE file for details.

Author

Henry Feng


For more examples and advanced usage, please check the documentation.