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

@vydra-js/http

v0.0.1

Published

HTTP client for making API requests with interceptors, automatic JSON handling, and integration with the Event Bus.

Readme

@vydra-js/http

HTTP client for making API requests with interceptors, automatic JSON handling, and integration with the Event Bus.

Installation

npm install @vydra-js/http

Quick Start

import { HttpBase } from '@vydra-js/http';

class ApiService extends HttpBase {
  constructor() {
    super({
      baseUrl: '/api',
    });
  }

  async getUsers() {
    return this.get('/users');
  }

  async createUser(data: any) {
    return this.post('/users', data);
  }

  async updateUser(id: string, data: any) {
    return this.put(`/users/${id}`, data);
  }

  async deleteUser(id: string) {
    return this.delete(`/users/${id}`);
  }
}

const api = new ApiService();
const users = await api.getUsers();

API

HttpBase

Base class for HTTP services.

class HttpBase {
  constructor(options?: HttpOptions);
}

Options

interface HttpOptions {
  baseUrl?: string;
  defaultHeaders?: Record<string, string>;
  interceptors?: HttpInterceptor[];
}

Methods

request<T>(config: HttpRequestConfig): Promise<HttpResponse<T>>

Generic request method.

const response = await api.request({
  method: 'GET',
  url: '/users',
});
get<T>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>>

GET request.

const users = await api.get('/users');
post<T>(url: string, data?: any, config?: HttpRequestConfig): Promise<HttpResponse<T>>

POST request.

const newUser = await api.post('/users', { name: 'John' });
put<T>(url: string, data?: any, config?: HttpRequestConfig): Promise<HttpResponse<T>>

PUT request.

const updated = await api.put('/users/1', { name: 'Jane' });
patch<T>(url: string, data?: any, config?: HttpRequestConfig): Promise<HttpResponse<T>>

PATCH request.

const patched = await api.patch('/users/1', { name: 'Jane' });
delete<T>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>>

DELETE request.

await api.delete('/users/1');

Interceptors

HttpInterceptor

interface HttpInterceptor {
  request?: (config: HttpRequestConfig) => HttpRequestConfig | Promise<HttpRequestConfig>;
  response?: (response: HttpResponse) => HttpResponse | Promise<HttpResponse>;
  error?: (error: any) => any | Promise<any>;
}

Adding Interceptors

class ApiService extends HttpBase {
  constructor() {
    super({
      baseUrl: '/api',
      interceptors: [
        {
          request: (config) => ({
            ...config,
            headers: {
              ...config.headers,
              Authorization: `Bearer ${token}`,
            },
          }),
        },
        {
          response: async (response) => {
            // Handle 401 responses
            if (response.status === 401) {
              // Redirect to login
            }
            return response;
          },
        },
      ],
    });
  }
}

Request Configuration

interface HttpRequestConfig {
  url: string;
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  headers?: Record<string, string>;
  body?: any;
  params?: Record<string, string>;
  timeout?: number;
}

Response

interface HttpResponse<T = any> {
  data: T;
  status: number;
  statusText: string;
  headers: Record<string, string>;
}

Concepts

Why HttpBase?

  • Extensible: Extend and customize per service
  • Interceptor-based: Add auth, logging, caching
  • Type-safe: Full TypeScript generics
  • Event Bus integration: Emit events for request/response

Request Flow

  1. Call get, post, etc.
  2. Apply request interceptors
  3. Execute fetch
  4. Apply response interceptors
  5. Return parsed response

Usage Examples

Auth Service

import { HttpBase, HttpInterceptor } from '@vydra-js/http';

class AuthService extends HttpBase {
  private token = '';

  constructor() {
    super({
      baseUrl: '/api',
      interceptors: [
        {
          request: (config) => ({
            ...config,
            headers: {
              ...config.headers,
              Authorization: `Bearer ${this.token}`,
            },
          }),
        },
      ],
    });
  }

  async login(credentials: any) {
    const response = await this.post('/auth/login', credentials);
    this.token = response.data.token;
    return response.data;
  }

  async logout() {
    await this.post('/auth/logout');
    this.token = '';
  }
}

User Service

import { HttpBase } from '@vydra-js/http';

interface User {
  id: string;
  name: string;
  email: string;
}

class UserService extends HttpBase {
  constructor() {
    super({ baseUrl: '/api' });
  }

  async getUsers(): Promise<User[]> {
    const response = await this.get<User[]>('/users');
    return response.data;
  }

  async getUser(id: string): Promise<User> {
    const response = await this.get<User>(`/users/${id}`);
    return response.data;
  }

  async createUser(user: Omit<User, 'id'>): Promise<User> {
    const response = await this.post<User>('/users', user);
    return response.data;
  }

  async updateUser(id: string, user: Partial<User>): Promise<User> {
    const response = await this.put<User>(`/users/${id}`, user);
    return response.data;
  }

  async deleteUser(id: string): Promise<void> {
    await this.delete(`/users/${id}`);
  }
}

Error Handling Interceptor

import { HttpBase, HttpInterceptor } from '@vydra-js/http';

const errorInterceptor: HttpInterceptor = {
  error: async (error) => {
    if (error.response?.status === 403) {
      console.error('Forbidden');
    } else if (error.response?.status === 500) {
      console.error('Server error');
    }
    throw error;
  },
};

Logging Interceptor

const loggingInterceptor: HttpInterceptor = {
  request: (config) => {
    console.log(`Request: ${config.method} ${config.url}`);
    return config;
  },
  response: (response) => {
    console.log(`Response: ${response.status}`);
    return response;
  },
};

Integration

With Event Bus

import { VydraBus } from '@vydra-js/bus';

class ApiService extends HttpBase {
  constructor() {
    super({
      baseUrl: '/api',
      interceptors: [
        {
          request: (config) => {
            new VydraBus('global').emit('http:request', config);
            return config;
          },
        },
      ],
    });
  }
}

With Forms

import { FormGroup } from '@vydra-js/forms';

class UserService extends HttpBase {
  async saveUser(form: FormGroup) {
    const data = form.value;
    return this.post('/users', data);
  }
}

Best Practices

  1. Create service classes per domain

    class UserService extends HttpBase { ... }
    class ProductService extends HttpBase { ... }
  2. Use interceptors for cross-cutting concerns

    // Auth, logging, error handling
  3. Handle errors appropriately

    try {
      await api.getUser(id);
    } catch (error) {
      // Handle 404, 500, etc.
    }
  4. Use type generics

    const users = await api.get<User[]>('/users');

Type Definitions

interface HttpOptions {
  baseUrl?: string;
  defaultHeaders?: Record<string, string>;
  interceptors?: HttpInterceptor[];
}

interface HttpRequestConfig {
  url: string;
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  headers?: Record<string, string>;
  body?: any;
  params?: Record<string, string>;
  timeout?: number;
}

interface HttpResponse<T = any> {
  data: T;
  status: number;
  statusText: string;
  headers: Record<string, string>;
}

See Also