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

nanoreq

v1.0.0

Published

Lightweight HTTP client library - tiny fetch wrapper with Axios-like API

Readme

NanoReq

CI/CD Pipeline npm version Bundle Size CodeQL License

Lightweight HTTP client library - A tiny fetch wrapper with an Axios-like API

Features

  • 🪶 Tiny bundle size - Zero dependencies, minimal overhead
  • 🌲 Tree-shakeable - Import only what you need
  • 🔄 Interceptors - Request and response interceptors support
  • 🎯 TypeScript first - Full TypeScript support with proper types
  • 🌐 Universal - Works in Node.js and browsers
  • Modern - Built on fetch API
  • 📦 Dual format - ESM and CJS exports

Installation

npm install nanoreq

Quick Start

import { nanoReq } from 'nanoreq';

// Simple GET request
const response = await nanoReq.get('https://api.example.com/users');
console.log(response.data);

// POST request with data
const newUser = await nanoReq.post('https://api.example.com/users', {
  name: 'John Doe',
  email: '[email protected]',
});

API Reference

Basic Methods

// GET request
nanoReq.get(url, config?)

// POST request
nanoReq.post(url, data?, config?)

// PUT request
nanoReq.put(url, data?, config?)

// PATCH request
nanoReq.patch(url, data?, config?)

// DELETE request
nanoReq.delete(url, config?)

// Generic request
nanoReq.request(config)

Creating Custom Instances

Create instances with default configuration (like axios.create):

import { createNanoReq } from 'nanoreq';

const api = createNanoReq({
  baseURL: 'https://api.example.com',
  headers: {
    Authorization: 'Bearer YOUR_TOKEN',
    'X-Custom-Header': 'value',
  },
  timeout: 5000,
});

// Now use the custom instance
const users = await api.get('/users');
const user = await api.get('/users/1');

Configuration Options

interface RequestConfig {
  baseURL?: string; // Base URL for requests
  url?: string; // Request URL
  method?: string; // HTTP method
  headers?: Record<string, string>; // Request headers
  params?: Record<string, any>; // URL query parameters
  data?: any; // Request body data
  timeout?: number; // Request timeout in ms
  rateLimit?: {
    // Rate limiting (per instance)
    maxRequests: number; // Max requests in time window
    perMilliseconds: number; // Time window in ms
  };
  retry?: {
    // Retry configuration
    count?: number; // Max retry attempts
    delay?: number; // Delay between retries
    exponentialBackoff?: boolean; // Enable exponential backoff
  };
  validateStatus?: (status: number) => boolean; // Custom status validator
  debug?: boolean; // Enable debug logging
}

Response Object

interface NanoReqResponse<T = any> {
  data: T; // Response data (auto-parsed JSON)
  status: number; // HTTP status code
  statusText: string; // HTTP status text
  headers: Headers; // Response headers
  config: RequestConfig; // Request configuration used
}

Error Object

interface NanoReqError extends Error {
  message: string; // Error message
  status?: number; // HTTP status code (if available)
  url?: string; // Request URL
  method?: string; // HTTP method
  data?: any; // Error response data
  originalError?: Error; // Original error object
}

Examples

Basic GET Request

import { nanoReq } from 'nanoreq';

try {
  const response = await nanoReq.get('https://jsonplaceholder.typicode.com/posts/1');
  console.log(response.data);
} catch (error) {
  console.error('Error:', error.message);
}

POST Request with Data

const response = await nanoReq.post('https://jsonplaceholder.typicode.com/posts', {
  title: 'My Post',
  body: 'This is the content',
  userId: 1,
});

console.log('Created:', response.data);

Using Query Parameters

const response = await nanoReq.get('https://api.example.com/search', {
  params: {
    q: 'javascript',
    page: 1,
    limit: 10,
  },
});
// Requests: https://api.example.com/search?q=javascript&page=1&limit=10

Custom Headers

const response = await nanoReq.get('https://api.example.com/protected', {
  headers: {
    Authorization: 'Bearer YOUR_TOKEN',
    'X-Custom-Header': 'value',
  },
});

Using BaseURL

import { createNanoReq } from 'nanoreq';

const api = createNanoReq({
  baseURL: 'https://api.github.com',
  headers: {
    Accept: 'application/vnd.github.v3+json',
  },
});

// All requests will be relative to baseURL
const repos = await api.get('/users/octocat/repos');
const repo = await api.get('/repos/octocat/hello-world');

Request Interceptors

Add custom logic before requests are sent:

import { createNanoReq } from 'nanoreq';

const api = createNanoReq({
  baseURL: 'https://api.example.com',
});

// Add authentication token to all requests
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers = {
      ...config.headers,
      Authorization: `Bearer ${token}`,
    };
  }
  return config;
});

// Add request logging
api.interceptors.request.use(config => {
  console.log(`Making ${config.method} request to ${config.url}`);
  return config;
});

// Now all requests will have the token
const response = await api.get('/protected-resource');

Response Interceptors

Transform or log responses:

import { createNanoReq } from 'nanoreq';

const api = createNanoReq({
  baseURL: 'https://api.example.com',
});

// Transform response data
api.interceptors.response.use(response => {
  // Unwrap nested data structure
  if (response.data && response.data.data) {
    response.data = response.data.data;
  }
  return response;
});

// Log response times
api.interceptors.request.use(config => {
  config.metadata = { startTime: Date.now() };
  return config;
});

api.interceptors.response.use(response => {
  const duration = Date.now() - response.config.metadata?.startTime;
  console.log(`Request took ${duration}ms`);
  return response;
});

Error Handling

import { nanoReq } from 'nanoreq';
import type { NanoReqError } from 'nanoreq';

try {
  const response = await nanoReq.get('https://api.example.com/not-found');
} catch (error) {
  const err = error as NanoReqError;

  if (err.status === 404) {
    console.log('Resource not found');
  } else if (err.status === 500) {
    console.log('Server error');
  } else if (!err.status) {
    console.log('Network error:', err.message);
  }

  console.log('Error details:', {
    url: err.url,
    method: err.method,
    status: err.status,
    data: err.data,
  });
}

Error Interceptors

Handle errors globally:

import { createNanoReq } from 'nanoreq';

const api = createNanoReq({
  baseURL: 'https://api.example.com',
});

api.interceptors.response.use(
  response => response,
  error => {
    if (error.status === 401) {
      // Redirect to login
      window.location.href = '/login';
    } else if (error.status >= 500) {
      // Show error notification
      console.error('Server error:', error.message);
    }
    throw error;
  }
);

Timeout

// Set timeout for a specific request
try {
  const response = await nanoReq.get('https://api.example.com/slow', {
    timeout: 3000, // 3 seconds
  });
} catch (error) {
  if (error.message.includes('timeout')) {
    console.log('Request timed out');
  }
}

// Set default timeout for all requests
const api = createNanoReq({
  baseURL: 'https://api.example.com',
  timeout: 5000,
});

Rate Limiting

Prevent API hammering with built-in rate limiting:

const api = createNanoReq({
  baseURL: 'https://api.example.com',
  rateLimit: {
    maxRequests: 10,
    perMilliseconds: 1000, // 10 requests per second
  },
});

// Requests are automatically rate-limited
const users = await api.get('/users');

Middleware Pattern

Add composable middleware for request processing:

const api = createNanoReq({
  baseURL: 'https://api.example.com',
});

// Add authentication middleware
api.use(async config => {
  const token = localStorage.getItem('token');
  if (!token) {
    return false; // Cancel request if no token
  }
  config.headers = {
    ...config.headers,
    Authorization: `Bearer ${token}`,
  };
  return config;
});

// Add logging middleware
api.use(async config => {
  console.log(`Making ${config.method} request to ${config.url}`);
  return config; // Continue with modified config
});

// Middleware runs before interceptors
const response = await api.get('/protected');

TypeScript Usage

Full type safety with generics:

import { nanoReq } from 'nanoreq';

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

interface Post {
  id: number;
  title: string;
  body: string;
  userId: number;
}

// Type-safe responses
const userResponse = await nanoReq.get<User>('https://api.example.com/users/1');
const user: User = userResponse.data;

const postsResponse = await nanoReq.get<Post[]>('https://api.example.com/posts');
const posts: Post[] = postsResponse.data;

Comparison: NanoReq vs Axios vs Ky

A detailed comparison of popular HTTP client libraries:

| Feature | NanoReq | Axios | Ky | | ------------------------- | ---------------------------------------- | -------------------------- | -------------------------------- | | Bundle Size | ~4.6 KB (gzipped) | ~13 KB | ~3 KB | | Dependencies | 0 | Several | 0 | | HTTP Methods | ✅ GET, POST, PUT, PATCH, DELETE | ✅ All methods | ✅ GET, POST, PUT, PATCH, DELETE | | Interceptors | ✅ Request & Response | ✅ Request & Response | ❌ No | | Middleware | ✅ Composable middleware | ❌ No | ❌ No | | Rate Limiting | ✅ Built-in | ❌ No | ❌ No | | Request Deduplication | ✅ Automatic for GET | ❌ No | ❌ No | | Retry Logic | ✅ Configurable | ❌ No (plugin) | ✅ Built-in | | TypeScript | ✅ Full support | ✅ Full support | ✅ Full support | | Config Merging | ✅ Instance & request | ✅ Instance & request | ✅ Partial | | Base URL | ✅ | ✅ | ✅ | | Timeout | ✅ | ✅ | ✅ | | Request Cancellation | ✅ AbortSignal | ✅ CancelToken/AbortSignal | ✅ AbortSignal | | Progress Events | ❌ No | ✅ Yes | ❌ No | | Security Features | ✅ SSRF protection, header validation | ❌ No | ❌ No | | Request Pipeline | ✅ Unified pipeline architecture | ❌ No | ❌ No | | Debug Mode | ✅ Built-in logging | ❌ No | ❌ No | | FormData Support | ✅ Auto-detection | ✅ Yes | ✅ Yes | | Response Transform | ✅ Hooks | ✅ Transform response | ❌ No | | Error Types | ✅ NetworkError, TimeoutError, HTTPError | ✅ AxiosError | ✅ HTTPError | | Browser Support | Modern browsers (fetch API) | All browsers | Modern browsers (fetch API) | | Node.js Support | Node 18+ (native fetch) | All versions | Node 18+ (native fetch) |

When to Use Each Library

Choose NanoReq if:

  • ✅ You need a lightweight Axios-like API
  • ✅ You want built-in rate limiting and request deduplication
  • ✅ You need security features for server-side usage
  • ✅ You prefer a unified request pipeline architecture
  • ✅ You want middleware support
  • ✅ You need comprehensive TypeScript support

Choose Axios if:

  • ✅ You need maximum compatibility (older browsers)
  • ✅ You need progress events for uploads/downloads
  • ✅ You have existing Axios code to migrate
  • ✅ You need a battle-tested, widely-used library

Choose Ky if:

  • ✅ You want the absolute smallest bundle size
  • ✅ You only need basic HTTP requests
  • ✅ You don't need interceptors or middleware
  • ✅ You prefer a simpler, more minimal API

Browser Support

NanoReq works in all modern browsers that support the Fetch API:

  • Chrome 42+
  • Firefox 39+
  • Safari 10.1+
  • Edge 14+

For older browsers, you'll need a fetch polyfill.

Node.js Support

Requires Node.js 18+ (native fetch support) or use a fetch polyfill like node-fetch for older versions.

License

MIT

Contributing

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