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

@digitalbooting/request-api

v1.0.8

Published

Light Weight Request Api for Http requests support GraphQL and Rest

Readme

@digitalbooting/request-api

npm version License: ISC

Lightweight HTTP client for JavaScript with built-in support for REST, GraphQL, encryption, middlewares, and authentication.

✨ Features

  • 🚀 Lightweight - Minimal dependencies, maximum performance
  • 🔄 REST & GraphQL - Full support for both paradigms
  • 🔐 Built-in Encryption - AES-256 encryption for sensitive data
  • 🎯 Middleware System - Chain operations before requests
  • 🔑 Authentication - Bearer and Basic auth helpers
  • 📦 FormData Support - File uploads made easy
  • Promise-based - Modern async/await syntax
  • 🎨 Response Pattern - Consistent [payload, error] destructuring
  • 🔄 Auto Header Reset - Preserves default headers across requests

📦 Installation

# npm
npm install @digitalbooting/request-api

# yarn
yarn add @digitalbooting/request-api

# pnpm
pnpm add @digitalbooting/request-api

🚀 Quick Start

Basic Usage

import { createClient } from '@digitalbooting/request-api';

// Create client instance
const api = createClient('https://api.example.com', {
  'Content-Type': 'application/json'
});

// Make a GET request
const [payload, error] = await api.get('/users');

if (error) {
  console.error('Request failed:', error);
} else {
  console.log('Users:', payload.data);
}

Using Class Constructor

import ApiClient from '@digitalbooting/request-api';

const api = new ApiClient('https://api.example.com', {
  'Content-Type': 'application/json',
  'X-Custom-Header': 'value'
});

📚 API Reference

Constructor

new ApiClient(baseURL, headers?, options?)

Parameters:

  • baseURL (string): Base URL for all requests
  • headers (object, optional): Default headers for all requests
  • options (object, optional): Additional fetch options

Example:

const api = new ApiClient('https://api.example.com', {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer token'
}, {
  mode: 'cors',
  credentials: 'include'
});

HTTP Methods

All methods return a Promise that resolves to [payload, error] tuple.

GET Request

await api.get(path)

Example:

const [payload, error] = await api.get('/users');

if (error) {
  console.error('Error:', error.data);
} else {
  console.log('Users:', payload.data);
  console.log('Status:', payload.status);
  console.log('Success:', payload.success);
}

POST Request

await api.post(path, body, isFormData?)

Example with JSON:

const [payload, error] = await api.post('/users', {
  name: 'John Doe',
  email: '[email protected]'
});

Example with FormData:

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('description', 'Profile picture');

const [payload, error] = await api.post('/upload', formData, true);

PUT Request

await api.put(path, body, isFormData?)

Example:

const [payload, error] = await api.put('/users/123', {
  name: 'Jane Doe'
});

DELETE Request

await api.delete(path)

Example:

const [payload, error] = await api.delete('/users/123');

PURE POST (No response parsing)

For endpoints that don't return JSON:

await api.pure_post(path, body, isFormData?)

Example:

const [payload, error] = await api.pure_post('/webhook', {
  event: 'user.created'
});

// payload.success indicates HTTP status
// payload.status contains the HTTP status code

GraphQL

await api.graphql(query, variables?, method?)

Parameters:

  • query (string): GraphQL query string
  • variables (object, optional): Query variables
  • method (string, optional): HTTP method (default: 'POST')

Example:

const query = `
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      posts {
        title
        content
      }
    }
  }
`;

const [payload, error] = await api.graphql(query, { id: '123' });

if (!error) {
  console.log('User:', payload.data.user);
}

Mutation Example:

const mutation = `
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

const [payload, error] = await api.graphql(mutation, {
  input: {
    name: 'John Doe',
    email: '[email protected]'
  }
});

Custom GraphQL Endpoint:

const api = new ApiClient('https://api.example.com', {}, {
  disableDefaultGraphqlEndpoint: true
});

// Requests will go to baseURL directly
// instead of baseURL/graphql

Headers Management

Set Headers

api.setHeaders(newHeaders)

Example:

api.setHeaders({
  'X-API-Key': 'your-api-key',
  'X-Custom-Header': 'value'
});

Reset to Default Headers

api.setDefaultHeaders()

Headers are automatically reset after each request to the values passed in the constructor.

Example:

// Initial headers
const api = createClient('https://api.example.com', {
  'Content-Type': 'application/json'
});

// Add temporary header
api.setHeaders({ 'X-Temp-Header': 'temp' });

// Make request
await api.get('/data');

// X-Temp-Header is automatically removed after request
// Content-Type is preserved for next request

Authentication

Bearer Token

api.setAuth('bearer', token)

Example:

const token = 'eyJhbGciOiJIUzI1NiIs...';
api.setAuth('bearer', token);

// All subsequent requests will include:
// Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Basic Auth

api.setAuth('basic', 'username:password')

Example:

api.setAuth('basic', 'admin:secretpass');

// All subsequent requests will include:
// Authorization: Basic YWRtaW46c2VjcmV0cGFzcw==

JWT Token Check

await api.checkJWToken(token)

Example:

const [isValid, error] = await api.checkJWToken(currentToken);

if (!isValid) {
  console.log('Token expired:', error);
  // Handle re-authentication
}

Middleware System

Middlewares are executed before each request. If a middleware returns false, the request is blocked.

Register Middleware

api.registerMiddleware(asyncFunction)

Example: Logging Middleware

api.registerMiddleware(async () => {
  console.log('Request initiated at:', new Date().toISOString());
  return true; // Continue with request
});

Example: Authentication Check

api.registerMiddleware(async () => {
  const token = localStorage.getItem('auth_token');
  
  if (!token) {
    console.error('No authentication token found');
    return false; // Block request
  }
  
  api.setAuth('bearer', token);
  return true; // Continue with request
});

Example: Rate Limiting

let lastRequestTime = 0;
const MIN_INTERVAL = 1000; // 1 second

api.registerMiddleware(async () => {
  const now = Date.now();
  const timeSinceLastRequest = now - lastRequestTime;
  
  if (timeSinceLastRequest < MIN_INTERVAL) {
    console.warn('Rate limit: Too many requests');
    return false;
  }
  
  lastRequestTime = now;
  return true;
});

Example: Multiple Middlewares

// Logger
api.registerMiddleware(async () => {
  console.log('[API] Request starting...');
  return true;
});

// Auth check
api.registerMiddleware(async () => {
  const token = getToken();
  if (!token) return false;
  api.setAuth('bearer', token);
  return true;
});

// Custom header
api.registerMiddleware(async () => {
  api.setHeaders({
    'X-Request-ID': generateRequestId()
  });
  return true;
});

Encryption (AES-256)

Built-in AES-256 encryption for sensitive data transmission.

Enable Encryption

api.enableEncryption(key?, iv?)

Parameters:

  • key (string, optional): 32-byte hex key (64 characters)
  • iv (string, optional): 16-byte hex IV (32 characters)

Example with Auto-generated Keys:

api.enableEncryption();

// Keys are auto-generated and accessible via:
console.log('Key:', api.key);
console.log('IV:', api.iv);

Example with Custom Keys:

const key = 'a'.repeat(64); // 32 bytes in hex
const iv = 'b'.repeat(32);  // 16 bytes in hex

api.enableEncryption(key, iv);

Set Encryption Key

api.setKey(hexKey)

Example:

const key = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
api.setKey(key); // Must be 64 hex characters (32 bytes)

Set Encryption IV

api.setIV(hexIV)

Example:

const iv = '0123456789abcdef0123456789abcdef';
api.setIV(iv); // Must be 32 hex characters (16 bytes)

Manual Encryption/Decryption

const encrypted = api.encrypt(plainText);
const decrypted = api.decrypt(encryptedText);

Example:

api.enableEncryption();

const sensitive = 'Credit card: 1234-5678-9012-3456';
const encrypted = api.encrypt(sensitive);
console.log('Encrypted:', encrypted);

const decrypted = api.decrypt(encrypted);
console.log('Decrypted:', decrypted); // Original text

Utility Methods

Get Client IP

await api.requestIp()

Example:

const ipInfo = await api.requestIp();
console.log('Client IP:', ipInfo.ip);

Getters

api.base_url              // Get base URL
api.headers_enabled       // Get current headers
api.options_enabled       // Get fetch options
api.middlewares_enabled   // Get registered middlewares
api.encryption_enabled    // Check if encryption is enabled
api.key                   // Get encryption key
api.iv                    // Get encryption IV

Example:

console.log('Base URL:', api.base_url);
console.log('Headers:', api.headers_enabled);
console.log('Middlewares:', api.middlewares_enabled.length);
console.log('Encrypted:', api.encryption_enabled);

🎯 Response Format

All HTTP methods return a tuple [payload, error]:

Success Response

const [payload, error] = await api.get('/users');

if (!error) {
  payload.data      // Response data
  payload.success   // true
  payload.status    // HTTP status code (200, 201, etc.)
}

Error Response

const [payload, error] = await api.post('/users', invalidData);

if (error) {
  error.data        // Error data from server
  error.response    // Raw response object
  error.success     // false
  error.status      // HTTP status code (400, 500, etc.)
}

Usage Pattern

// Destructure and check
const [payload, error] = await api.get('/users');

if (error) {
  console.error('Request failed:', error.data);
  return;
}

console.log('Success:', payload.data);

💡 Advanced Examples

Complete Authentication Flow

import { createClient } from '@digitalbooting/request-api';

const api = createClient('https://api.example.com', {
  'Content-Type': 'application/json'
});

// Login
async function login(email, password) {
  const [payload, error] = await api.post('/auth/signin', {
    email,
    password
  });
  
  if (error) {
    throw new Error(error.data?.message || 'Login failed');
  }
  
  // Store token
  localStorage.setItem('auth_token', payload.data.token);
  
  // Set for future requests
  api.setAuth('bearer', payload.data.token);
  
  return payload.data.user;
}

// Logout
async function logout() {
  const [payload, error] = await api.post('/auth/signout');
  
  localStorage.removeItem('auth_token');
  api.setDefaultHeaders(); // Clear auth header
  
  return !error;
}

// Auto-attach token middleware
api.registerMiddleware(async () => {
  const token = localStorage.getItem('auth_token');
  
  if (token) {
    api.setAuth('bearer', token);
  }
  
  return true;
});

File Upload with Progress

async function uploadFile(file, onProgress) {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('timestamp', Date.now());
  
  // Add progress tracking middleware
  api.registerMiddleware(async () => {
    if (onProgress) {
      onProgress({ status: 'uploading', progress: 0 });
    }
    return true;
  });
  
  const [payload, error] = await api.post('/upload', formData, true);
  
  if (error) {
    if (onProgress) {
      onProgress({ status: 'error', error: error.data });
    }
    return null;
  }
  
  if (onProgress) {
    onProgress({ status: 'complete', data: payload.data });
  }
  
  return payload.data;
}

// Usage
uploadFile(myFile, (progress) => {
  console.log('Upload:', progress);
});

API Service Class

import { createClient } from '@digitalbooting/request-api';

class UserService {
  constructor(baseURL) {
    this.api = createClient(baseURL, {
      'Content-Type': 'application/json'
    });
    
    this.setupMiddlewares();
  }
  
  setupMiddlewares() {
    // Auth middleware
    this.api.registerMiddleware(async () => {
      const token = this.getToken();
      if (token) {
        this.api.setAuth('bearer', token);
      }
      return true;
    });
    
    // Logging middleware
    this.api.registerMiddleware(async () => {
      console.log('[UserService] Request initiated');
      return true;
    });
  }
  
  getToken() {
    return localStorage.getItem('auth_token');
  }
  
  async getAll() {
    const [payload, error] = await this.api.get('/users');
    if (error) throw new Error(error.data?.message);
    return payload.data;
  }
  
  async getById(id) {
    const [payload, error] = await this.api.get(`/users/${id}`);
    if (error) throw new Error(error.data?.message);
    return payload.data;
  }
  
  async create(userData) {
    const [payload, error] = await this.api.post('/users', userData);
    if (error) throw new Error(error.data?.message);
    return payload.data;
  }
  
  async update(id, userData) {
    const [payload, error] = await this.api.put(`/users/${id}`, userData);
    if (error) throw new Error(error.data?.message);
    return payload.data;
  }
  
  async delete(id) {
    const [payload, error] = await this.api.delete(`/users/${id}`);
    if (error) throw new Error(error.data?.message);
    return payload.success;
  }
}

// Usage
const userService = new UserService('https://api.example.com');

const users = await userService.getAll();
const user = await userService.create({ name: 'John', email: '[email protected]' });

GraphQL with Encryption

const api = createClient('https://api.example.com');

// Enable encryption
api.enableEncryption();

// Send encrypted GraphQL query
const query = `
  query GetSensitiveData {
    user {
      ssn
      creditCard
    }
  }
`;

const [payload, error] = await api.graphql(query);

// The query is automatically encrypted before sending
// Response is automatically decrypted (if server supports it)

Error Handling Patterns

// Pattern 1: Inline checking
const [payload, error] = await api.get('/users');

if (error) {
  console.error('Error:', error.data);
  return;
}

console.log('Data:', payload.data);

// Pattern 2: Try-catch wrapper
async function fetchUsers() {
  const [payload, error] = await api.get('/users');
  
  if (error) {
    throw new Error(error.data?.message || 'Failed to fetch users');
  }
  
  return payload.data;
}

try {
  const users = await fetchUsers();
  console.log('Users:', users);
} catch (err) {
  console.error('Error:', err.message);
}

// Pattern 3: Custom error handler
function handleApiError(error) {
  if (error.status === 401) {
    // Unauthorized - redirect to login
    window.location.href = '/login';
  } else if (error.status === 403) {
    // Forbidden
    alert('You do not have permission to perform this action');
  } else if (error.status === 500) {
    // Server error
    console.error('Server error:', error.data);
  } else {
    // Generic error
    console.error('Request failed:', error.data);
  }
}

const [payload, error] = await api.get('/users');

if (error) {
  handleApiError(error);
  return;
}

console.log('Data:', payload.data);

🔧 Configuration

Fetch Options

You can pass any valid fetch options:

const api = createClient('https://api.example.com', {
  'Content-Type': 'application/json'
}, {
  mode: 'cors',
  credentials: 'include',
  cache: 'no-cache',
  redirect: 'follow',
  referrerPolicy: 'no-referrer'
});

Custom Options per Request

api.setOptions({
  signal: abortController.signal,
  priority: 'high'
});

const [payload, error] = await api.get('/users');

📝 Best Practices

1. Use Destructuring Pattern

// ✅ Good
const [payload, error] = await api.get('/users');

if (error) {
  // Handle error
}

// ❌ Bad
const response = await api.get('/users');

if (response[1]) {
  // Handle error
}

2. Check Errors First

// ✅ Good
const [payload, error] = await api.post('/users', data);

if (error) {
  console.error('Failed:', error.data);
  return;
}

console.log('Success:', payload.data);

// ❌ Bad
const [payload, error] = await api.post('/users', data);

console.log('Success:', payload.data); // May crash if error

3. Reuse Client Instances

// ✅ Good - Single instance
// api.js
export const api = createClient('https://api.example.com', {
  'Content-Type': 'application/json'
});

// users.js
import { api } from './api';
const [payload, error] = await api.get('/users');

// ❌ Bad - Multiple instances
const api1 = createClient('https://api.example.com');
const api2 = createClient('https://api.example.com');

4. Use Middlewares for Cross-cutting Concerns

// ✅ Good - Centralized auth
api.registerMiddleware(async () => {
  const token = getToken();
  if (token) api.setAuth('bearer', token);
  return true;
});

// ❌ Bad - Manual auth each time
api.setAuth('bearer', getToken());
await api.get('/users');

api.setAuth('bearer', getToken());
await api.get('/posts');

5. Handle FormData Correctly

// ✅ Good
const formData = new FormData();
formData.append('file', file);

const [payload, error] = await api.post('/upload', formData, true);
//                                                             ^^^^ isFormData

// ❌ Bad - Will stringify FormData
const [payload, error] = await api.post('/upload', formData);

🐛 Troubleshooting

Headers Not Persisting

Problem: Headers are lost after first request.

Solution: This was fixed in v1.0.7. Make sure you're using the latest version:

npm install @digitalbooting/request-api@latest

CORS Errors

Problem: Request blocked by CORS policy.

Solution: Configure fetch options:

const api = createClient('https://api.example.com', {
  'Content-Type': 'application/json'
}, {
  mode: 'cors',
  credentials: 'include'
});

FormData Not Sending

Problem: FormData sent as JSON.

Solution: Pass true as third parameter:

const [payload, error] = await api.post('/upload', formData, true);
//                                                             ^^^^

Middleware Blocking Requests

Problem: Request never executes.

Solution: Ensure middleware returns true:

api.registerMiddleware(async () => {
  // Your logic here
  return true; // ← Must return true to continue
});

📊 Changelog

v1.0.7 (Current)

  • 🐛 Fix: Default headers now properly preserved across requests
  • 🔧 Fix: #initialHeaders correctly initialized in constructor
  • Improvement: Headers automatically reset to constructor values after each request

v1.0.6

  • 🐛 Bug: Headers reset to empty object after each request (fixed in 1.0.7)

📄 License

ISC © Leonardo Quintana


🤝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Push to the branch
  5. Open a Pull Request

📮 Support


🌟 Show Your Support

Give a ⭐️ if this project helped you!


Made with ❤️ by Digital Booting