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

@suamnkayal/reqnest

v1.0.7

Published

A middleware-driven HTTP client for JavaScript (Node.js + Browser)

Readme

npm version License: MIT TypeScript PRs Welcome Made in India

Build, control, and extend HTTP requests like a backend engineer.

Reqnest gives you full ownership over the request lifecycle — nothing is hidden, everything is composable.

Getting Started · Plugins · API Docs · vs Axios · Roadmap


🤔 Why Reqnest?

Most HTTP clients treat the request lifecycle as an implementation detail. You get a black box that usually works — until you need something custom, and then you're monkey-patching interceptors and praying nothing breaks.

Reqnest is different. It exposes the full pipeline through a clean middleware system, so you can add caching, retries, deduplication, and rate limiting as composable layers — without touching the core.

Your App  →  [dedupe]  →  [cache]  →  [rateLimit]  →  [dispatch]  →  Network
         ←  [dedupe]  ←  [cache]  ←  [rateLimit]  ←  [dispatch]  ←

✨ Features


📦 Installation

npm install @suamnkayal/reqnest
# or
yarn add @suamnkayal/reqnest
# or
pnpm add @suamnkayal/reqnest

Requires Node.js 18+ or a modern browser with native fetch support.


🚀 Quick Start

import reqnest from "@suamnkayal/reqnest";

const res = await reqnest.get("https://jsonplaceholder.typicode.com/posts");

console.log(res.data);    // parsed JSON
console.log(res.status);  // 200

⚙️ Custom Instance

Create isolated client instances with their own base URL, headers, and middleware stack.

import { create, dispatch } from "@suamnkayal/reqnest";

const api = create({
  baseURL: "https://api.example.com",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer YOUR_TOKEN",
  },
  timeout: 8000,
});

// dispatch must always be the LAST middleware — it sends the actual request
api.use(dispatch);

const res = await api.get("/users");

⚠️ Always register dispatch last. It is the terminal handler that performs the real HTTP call.


🔌 Plugins

Plugins are the superpower of Reqnest. Chain them in any order — each one is a focused, reusable middleware.

import { create, dispatch, cachePlugin, dedupe, rateLimit } from "reqnest";

const api = create({ baseURL: "https://api.example.com" });

api.use(dedupe);        // 🔄 collapse identical concurrent requests into one
api.use(cachePlugin);   // 🧠 cache responses, skip repeat network calls
api.use(rateLimit(3));  // 🚦 max 3 concurrent requests at any time
api.use(dispatch);      // 📡 send the request (always last)

Plugin Execution Order

Middleware runs top-to-bottom on the way out, and bottom-to-top on the way back:

Request  →  dedupe  →  cache  →  rateLimit  →  dispatch  →  🌐
Response ←  dedupe  ←  cache  ←  rateLimit  ←  dispatch  ←  🌐

📡 Request Methods

// GET
await api.get("/posts");

// POST
await api.post("/posts", {
  title: "Hello Reqnest",
  body: "Middleware is everything.",
});

// PUT — full replacement
await api.put("/posts/1", { id: 1, title: "New Title", body: "New body" });

// PATCH — partial update
await api.patch("/posts/1", { title: "Just the title" });

// DELETE
await api.delete("/posts/1");

📘 API Reference

Request Config

api.get("/users", {
  params: { page: 1, limit: 10 },       // → appended as ?page=1&limit=10
  headers: { Authorization: "Bearer token" },
  timeout: 5000,                         // abort after 5 seconds
  retry: 3,                              // retry up to 3 times on failure
  cache: true,                           // use cachePlugin if registered
  signal: controller.signal,            // manual AbortSignal
  transformRequest: (data) => data,      // transform outgoing body
  transformResponse: (data) => data,     // transform incoming response
});

Full Config Type

interface ReqnestConfig {
  url: string;
  method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
  baseURL?: string;

  headers?: Record<string, string>;
  params?: Record<string, any>;
  data?: any;

  timeout?: number;
  signal?: AbortSignal;
  retry?: number;
  cache?: boolean;

  transformRequest?: (data: any) => any;
  transformResponse?: (data: any) => any;
}

Response Format

interface ReqnestResponse<T = any> {
  data: T;                          // parsed JSON body
  status: number;                   // e.g. 200
  statusText: string;               // e.g. "OK"
  headers: Record<string, string>;  // response headers
  raw: Response;                    // original fetch Response object
}

❌ Error Handling

Reqnest throws structured errors on all non-2xx responses. No more manually checking response.ok.

try {
  await api.get("/not-found");
} catch (err) {
  console.log(err.message);  // "Not Found"
  console.log(err.status);   // 404
  console.log(err.data);     // server error body (if any)
}

🧩 Middleware System

Write your own middleware to hook into any part of the request lifecycle.

// Signature
type Middleware = (ctx: Context, next: () => Promise<void>) => Promise<void>;

interface Context {
  config: ReqnestConfig;        // outgoing request config (mutate to modify)
  response?: ReqnestResponse;   // set after dispatch runs
}

Auth Middleware

api.use(async (ctx, next) => {
  const token = localStorage.getItem("token");
  if (token) {
    ctx.config.headers = {
      ...ctx.config.headers,
      Authorization: `Bearer ${token}`,
    };
  }
  await next();
});

Logging Middleware

api.use(async (ctx, next) => {
  const start = Date.now();
  console.log(`→ ${ctx.config.method} ${ctx.config.url}`);

  await next();

  const ms = Date.now() - start;
  console.log(`← ${ctx.response.status} in ${ms}ms`);
});

Error Reporting Middleware

api.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    myErrorTracker.capture({ url: ctx.config.url, status: err.status });
    throw err; // always re-throw so callers still receive the error
  }
});

🔬 Plugin Deep Dives

🔁 Retry

Auto-retries on network error or 5xx. Won't retry 4xx client errors.

// Retry up to 3 times before throwing
await api.get("/flaky-endpoint", { retry: 3 });

🧠 Cache

Caches responses in-memory by URL + query params. Second identical request is instant — zero network.

api.use(cachePlugin);

const a = await api.get("/posts");  // → network call
const b = await api.get("/posts");  // → cache hit ⚡ (no network)

🔄 Deduplication

Collapses simultaneous identical requests into a single network call. All callers resolve with the same data.

api.use(dedupe);

// 3 requests fire at the same time
const [a, b, c] = await Promise.all([
  api.get("/posts"),
  api.get("/posts"),
  api.get("/posts"),
]);
// ☝️ Only ONE network request was made

🚦 Rate Limiting

Queues requests and enforces a concurrency cap to protect your server or stay within API limits.

api.use(rateLimit(2)); // max 2 in-flight at a time

await Promise.all([
  api.get("/item/1"),  // ← starts immediately
  api.get("/item/2"),  // ← starts immediately
  api.get("/item/3"),  // ← queued
  api.get("/item/4"),  // ← queued
]);

⚖️ Reqnest vs Axios

| Feature | Reqnest | Axios | |---|:---:|:---:| | Middleware pipeline | ✅ | ❌ | | Plugin system | ✅ | ❌ | | Full lifecycle control | ✅ | ❌ | | Built on Fetch API | ✅ | ❌ (XHR) | | Request deduplication | ✅ | ❌ | | Smart caching | ✅ | ❌ | | Rate limiting | ✅ | ❌ | | TypeScript support | ✅ | ✅ | | Extensibility | 🔥 High | ⚠️ Limited |


🏗 Architecture

Reqnest is inspired by three ideas:

| Inspiration | What it contributes | |---|---| | Koa.js | Async middleware composition with next() | | Fetch API | Native, modern transport layer | | Redux middleware | Predictable, composable pipeline |

The core engine is intentionally tiny. Every advanced feature lives in its own isolated plugin — you only pay the cost of what you register.


📂 Project Structure

reqnest/
├── src/
│   ├── core/
│   │   ├── client.ts        # create() factory and instance logic
│   │   ├── compose.ts       # middleware composition engine
│   │   ├── dispatch.ts      # fetch-based terminal middleware
│   │   └── context.ts       # Context type definitions
│   │
│   ├── plugins/
│   │   ├── cache.ts         # in-memory caching
│   │   ├── dedupe.ts        # request deduplication
│   │   ├── rateLimit.ts     # concurrency limiting
│   │   └── retry.ts         # auto-retry logic
│   │
│   ├── types/
│   │   └── index.ts         # shared TypeScript interfaces
│   │
│   └── utils/
│       ├── buildURL.ts      # URL + query param serialization
│       └── parseResponse.ts # response parsing helpers
│
├── tests/
├── package.json
├── tsconfig.json
└── README.md

🗺 Roadmap

  • [ ] 🔥 Interceptors — request/response hooks (cleaner than Axios)
  • [ ] 🔥 GraphQL support — first-class gql() helper
  • [ ] 🔥 SSR support — cookie forwarding for server-side rendering
  • [ ] 🔥 Devtools — browser extension for request inspection
  • [ ] 🔥 Persistent cache — IndexedDB / localStorage adapter
  • [ ] 🔥 npm publish — official release on the npm registry

👨‍💻 Author

Suman Kayal · Sky

Full-Stack Developer · MERN Stack · Real-time Systems · AI Tooling

GitHub LinkedIn Instagram

Built in Kolkata, India 🇮🇳


⭐ Support

If Reqnest saved you time or taught you something:

  • Star this repo
  • 🍴 Fork it and build something cool
  • 📣 Share it with your dev community
  • 🐛 Open issues for bugs or feature ideas
  • 🤝 Contribute — PRs are always welcome

📄 License

MIT © Suman Kayal


Built with obsession. Designed for control. Made to be extended.