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

reactive-query-z

v3.2.0

Published

A lightweight React data-fetching library with caching, mutations, REST & GraphQL support, and request deduplication.

Readme

🗄️ reactive-query-z

NPM Downloads

LIVE EXAMPLE

reactive-query-z is a lightweight, reactive data-fetching library for React, built with hooks and TypeScript in mind.

Minimal API surface, predictable behavior, and production-ready features.


✨ Why reactive-query-z

  • ⚡ Lightweight & hook-based
  • 🔁 REST + GraphQL support
  • ✍️ Full CRUD mutations
  • 💾 Cache with global invalidation
  • 🚀 Optimistic UI updates
  • ♻️ Stale-while-revalidate
  • 📡 Real-time subscriptions (WebSocket / SSE)
  • 🧩 Middleware system (auth, logging, retry, timeout…)
  • 🧠 TypeScript-first API

Note: For full-featured query management, see React Query


📦 Installation

npm install reactive-query-z
# or
yarn add reactive-query-z

🚀 Basic Usage

1️⃣ Querying REST Data

import { useQuery, queryRegistry } from "reactive-query-z";

type User = { id: string; title: string };

function UserList() {
  const {
    data: users,
    loading,
    refetch,
    error,
  } = useQuery<User[]>("https://jsonplaceholder.typicode.com/posts", {
    cacheKey: "users-list",
    // staleTime: 5000,
    cacheTime: 10000,
    // autoFetch: true,
    headers: { "Content-Type": "application/json" },
  });

  return (
    <div>
      {loading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}

      <button onClick={() => refetch()}>Refetch</button>
      <button onClick={() => queryRegistry.invalidate("users-list")}>
        Invalidate
      </button>

      <ul>
        {users?.map((u) => (
          <li key={u.id}>{u.title}</li>
        ))}
      </ul>
    </div>
  );
}

2️⃣ GraphQL Query

import { useGraphQLQuery } from "reactive-query-z";

type Country = { code: string; name: string; emoji: string };

export function CountriesList() {
  const { data, loading, error, refetch } = useGraphQLQuery<{ countries: Country[] }>(
    "https://countries.trevorblades.com/",
    {
      query: `
        query {
          countries { code name emoji }
        }
      `,
      cacheKey: "countries",
    }
  );

  if (loading) return <p>Loading...</p>;
  if (error) return <p>{error.message}</p>;

  return (
    <ul>
      <button disabled={loading} onClick={refetch}>Refetch</button>
      <br />
      {data?.countries?.map(c => (
        <li key={c.code}>{c.name} {c.emoji}</li>
      ))}
    </ul>
  );
}

3️⃣ Mutations (REST)

import { useMutation, queryRegistry } from "reactive-query-z";

type Post = {
  userId: number;
  id: number;
  title: string;
  body: string;
};

export function AddPost() {
  const { mutate, loading } = useMutation<Post>(
    "https://jsonplaceholder.typicode.com/posts",
    {
      cacheKey: "posts",
      optimisticUpdate: (prev, newPost) => newPost,
      onSuccess: () => queryRegistry.invalidate("posts"),
    }
  );

  const handleAdd = () => {
    mutate({
      title: "New Post",
      body: "This is a new post",
      userId: 1,
    });
  };

  return (
    <button onClick={handleAdd} disabled={loading}>
      {loading ? "Adding..." : "Add Post"}
    </button>
  );
}

4️⃣ GraphQL Mutation

import { useGraphQLMutation, queryRegistry } from "reactive-query-z";

export function AddPostGraphQL() {
  const { mutate, loading } = useGraphQLMutation(
    "https://graphqlzero.almansi.me/api",
    {
      mutation: `
        mutation ($title: String!, $body: String!, $userId: ID!) {
          createPost(input: { title: $title, body: $body, userId: $userId }) {
            id
            title
            body
          }
        }
      `,
      variables: { title: "Hello", body: "World", userId: 122 },
      onSuccess: () => queryRegistry.invalidate("postsGraphQL"),
    }
  );

  return (
    <button onClick={mutate} disabled={loading}>
      {loading ? "Adding..." : "Add Post"}
    </button>
  );
}

5️⃣ Global Query Invalidation & Global Error Handling

import { queryRegistry, setGlobalErrorHandler } from "reactive-query-z";

queryRegistry.invalidate("users"); // specific
queryRegistry.invalidate();        // all queries

setGlobalErrorHandler((error, info) => {
  console.error("Global fetch error:", error, info);
});

6️⃣ Real-time Subscriptions

import { useHybridQuery } from "reactive-query-z";

const { data } = useHybridQuery("/graphql", {
  subscriptionUrl: "ws://localhost:4000",
  cacheKey: "messages",
});

7️⃣ Prefetch & Request Deduplication

🔹 queryClient.fetchQuery

import { queryClient } from "reactive-query-z";

await queryClient.fetchQuery<User[]>(
  "https://jsonplaceholder.typicode.com/users",
  { cacheKey: "users" }
);

🔹 queryClient.ensureQueryData (recommended)

import { queryClient, useQuery } from "reactive-query-z";

type User = { id: number; name: string };

async function preloadUsers() {
  await queryClient.ensureQueryData<User[]>(
    "https://jsonplaceholder.typicode.com/users",
    { cacheKey: "users" }
  );
}

function Users() {
  const { data, loading } = useQuery<User[]>(
    "https://jsonplaceholder.typicode.com/users",
    { cacheKey: "users" }
  );

  if (loading) return <p>Loading...</p>;

  return (
    <ul>
      {data?.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

🔧 API Reference

useQuery
useMutation
useGraphQLQuery
useGraphQLMutation
useHybridQuery

queryClient
queryRegistry

prefetchQuery
prefetchData
fetchWithRetry
commonFetch
setGlobalErrorHandler

⚖️ Comparison with Other Libraries

| Feature | reactive-query-z | React Query | SWR | Apollo Client | | ------------------------- | ---------------- | ----------- | ---- | ------------- | | REST support | ✅ | ✅ | ✅ | ⚠️ Partial | | GraphQL support | ✅ | ⚠️ Plugin | ❌ | ✅ | | Real-time subscription | ✅ | ⚠️ Plugin | ❌ | ✅ | | Global cache invalidation | ✅ | ✅ | ⚠️ | ✅ | | Optimistic updates | ✅ | ✅ | ⚠️ | ✅ | | Stale-while-revalidate | ✅ | ✅ | ✅ | ✅ | | Full CRUD mutations | ✅ | ✅ | ❌ | ✅ | | TypeScript-first | ✅ | ✅ | ✅ | ✅ | | Lightweight | ✅ | ⚠️ | ✅ | ⚠️ | | Subscription built-in | ✅ | ❌ | ❌ | ✅ |


When to use

  • You want full control over async orchestration
  • You dislike heavy cache lifecycles
  • You prefer explicit invalidation

When NOT to use

  • You need background refetch
  • You need offline persistence

📜 License

MIT