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

query-harbor

v0.1.0

Published

A React Query utility package for efficient data fetching and caching

Readme

Query Harbor

A collection of custom React hooks built on top of TanStack Query (formerly React Query) for handling API requests, mutations, and cookie management. These hooks provide a standardized way to handle data fetching, caching, and state management in React applications.

Table of Contents

Installation

# Install required dependencies
npm install @tanstack/react-query react-cookie axios

Hooks Overview

useGlobalQuery

A custom hook for making standard API requests with built-in caching and state management.

Features

  • Automatic authentication header handling
  • Configurable cache and stale times
  • Built-in error handling
  • Query invalidation support

Basic Usage

const {
    queryData,
    isLoading,
    isError,
    error,
    refetchQuery
} = useGlobalQuery({
    url: '/api/users',
    queryKey: ['users'],
    methodType: 'GET'
});

useGlobalMutation

A custom hook for handling data mutations (create, update, delete operations) with support for FormData.

Features

  • FormData support with nested object handling
  • Automatic query invalidation after successful mutation
  • Priority data support
  • Built-in error handling

Basic Usage

const {
    runMutation,
    mutationLoading,
    mutationData,
    mutationError,
    isMutationSucceeded
} = useGlobalMutation({
    url: '/api/users',
    queriesToInvalidate: ['users'],
    methodType: 'POST',
    data: userData
});

FormData Upload Examples

Basic FormData Upload
const {
    runMutation,
    mutationLoading
} = useGlobalMutation({
    url: '/api/upload',
    queriesToInvalidate: ['files'],
    methodType: 'POST',
    isFormData: true,
    data: {
        file: fileObject,
        title: 'My Document'
    }
});
Complex FormData with Arrays
// Without excludedIndexKeys
const data = {
    files: [file1, file2],
    metadata: {
        titles: ['Doc 1', 'Doc 2']
    }
};

// This will generate FormData with structure:
// files[0] = file1
// files[1] = file2
// metadata[titles][0] = Doc 1
// metadata[titles][1] = Doc 2
Using excludedIndexKeys
const MultipleFileUpload = () => {
    const {
        runMutation,
        mutationLoading
    } = useGlobalMutation({
        url: '/api/upload-multiple',
        queriesToInvalidate: ['files'],
        methodType: 'POST',
        isFormData: true,
        // Specify which keys should not include array indices
        excludedIndexKeys: ['files', 'documents'],
        data: {
            files: [file1, file2, file3],
            documents: [docFile1, docFile2],
            metadata: {
                titles: ['Doc 1', 'Doc 2', 'Doc 3'],
                categories: ['Cat 1', 'Cat 2', 'Cat 3']
            }
        }
    });

    return (
        <button onClick={() => runMutation()}>
            Upload Files
        </button>
    );
};

The above example will generate FormData with the following structure:

// Keys with excludedIndexKeys:
files = file1
files = file2
files = file3
documents = docFile1
documents = docFile2

// Regular array keys (maintain indices):
metadata[titles][0] = Doc 1
metadata[titles][1] = Doc 2
metadata[titles][2] = Doc 3
metadata[categories][0] = Cat 1
metadata[categories][1] = Cat 2
metadata[categories][2] = Cat 3
Real-World Example: Multiple File Upload with Metadata
const DocumentUploadForm = () => {
    const [files, setFiles] = useState([]);
    const [metadata, setMetadata] = useState({
        department: 'HR',
        tags: ['confidential', 'employee'],
    });

    const {
        runMutation,
        mutationLoading,
        isMutationSucceeded
    } = useGlobalMutation({
        url: '/api/documents/upload',
        queriesToInvalidate: ['documents'],
        methodType: 'POST',
        isFormData: true,
        excludedIndexKeys: ['files'], // files will be sent without indices
        data: {
            files: files,
            metadata: metadata,
            timestamp: new Date().toISOString(),
            user: {
                id: currentUserId,
                role: userRole
            }
        }
    });

    const handleFileChange = (e) => {
        setFiles(Array.from(e.target.files));
    };

    const handleUpload = () => {
        runMutation();
    };

    return (
        <div>
            <input 
                type="file" 
                multiple 
                onChange={handleFileChange} 
            />
            {mutationLoading ? (
                <p>Uploading...</p>
            ) : (
                <button onClick={handleUpload}>
                    Upload Documents
                </button>
            )}
            {isMutationSucceeded && (
                <p>Upload completed successfully!</p>
            )}
        </div>
    );
};

This will generate FormData where:

  • Multiple files are sent with the same key name ('files')
  • Metadata is properly nested with array indices preserved
  • Additional data is structured appropriately

The resulting FormData structure will be:

files = File1
files = File2
metadata[department] = HR
metadata[tags][0] = confidential
metadata[tags][1] = employee
timestamp = 2024-02-23T10:00:00.000Z
user[id] = 123
user[role] = admin

When to Use excludedIndexKeys

Use excludedIndexKeys when:

  1. Working with file upload APIs that expect multiple files with the same key
  2. Dealing with legacy APIs that don't support indexed form fields
  3. Implementing multi-file upload where the server expects a flat structure
  4. Handling file arrays where order doesn't matter

useGlobalInfiniteQuery

A custom hook for handling infinite scroll or pagination scenarios.

Features

  • Automatic pagination handling
  • Built-in cache management
  • Total count tracking
  • Next page detection

Basic Usage

const {
    queryData,
    isLoading,
    isError,
    error,
    fetchNextPage,
    hasNextPage,
    totalCount
} = useGlobalInfiniteQuery({
    url: '/api/posts',
    queryKey: ['posts'],
    methodType: 'GET',
    data: { limit: 10 }
});

useCookie

A utility hook for managing cookies across the application.

Features

  • Simple cookie management
  • Type-safe cookie operations
  • Easy integration with authentication

Basic Usage

const { cookie, setCookie, removeCookie } = useCookie({
    cookieName: 'accessToken'
});

API Reference

useGlobalQuery Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | url | string | required | API endpoint URL | | queryKey | string[] | required | Unique key for caching | | methodType | string | required | HTTP method (GET, POST, etc.) | | data | object | optional | Request payload | | enabled | boolean | true | Whether to enable the query | | cacheTime | number | 300000 | Cache duration in ms | | staleTime | number | 300000 | Stale data duration in ms |

useGlobalMutation Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | url | string | required | API endpoint URL | | queriesToInvalidate | string[] | required | Queries to refresh after mutation | | methodType | string | required | HTTP method (POST, PUT, DELETE) | | data | object | optional | Default mutation data | | isFormData | boolean | false | Whether to handle as FormData | | closePopup | function | optional | Callback after success | | excludedIndexKeys | string[] | optional | Keys to exclude from FormData indices |

useGlobalInfiniteQuery Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | url | string | required | API endpoint URL | | queryKey | string[] | required | Unique key for caching | | methodType | string | required | HTTP method | | data | object | optional | Additional query parameters | | enabled | boolean | true | Whether to enable the query | | cacheTime | number | 300000 | Cache duration in ms | | staleTime | number | 300000 | Stale data duration in ms |

Best Practices

  1. Always provide unique and descriptive query keys
  2. Set appropriate cache and stale times based on data volatility
  3. Handle loading and error states in your UI
  4. Use the refetchQuery function for manual data refresh
  5. Implement proper error boundaries in your application

Contributing

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

License

MIT