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

@consolify/api

v0.1.7

Published

A powerful and flexible API client package built on top of TanStack React Query and react-query-kit. This package provides a type-safe fetcher function with automatic URL parameter replacement, configurable base URLs, and seamless integration with React Q

Readme

@consolify/api

A powerful and flexible API client package built on top of TanStack React Query and react-query-kit. This package provides a type-safe fetcher function with automatic URL parameter replacement, configurable base URLs, and seamless integration with React Query.

Features

  • 🚀 Type-safe API calls with full TypeScript support
  • 🔧 Configurable base URL - set once per app, use everywhere
  • 🎯 Automatic URL parameter replacement - use :param syntax in URLs
  • 📦 Built on TanStack React Query - leverage the power of React Query
  • 🔄 Support for all HTTP methods (GET, POST, PUT, DELETE, etc.)
  • 📝 Automatic JSON serialization for request bodies
  • 📋 FormData support for file uploads
  • 🛡️ Error handling with proper error propagation

Installation

npm install @consolify/api
# or
yarn add @consolify/api
# or
pnpm add @consolify/api

Quick Start

1. Configure the Base URL

Configure the API base URL once in your app initialization (e.g., in main.tsx or App.tsx):

import { configureApi } from '@consolify/api';

// Configure the base URL for your app
configureApi({
  baseUrl: 'https://api.yourapp.com'
  // or for development: 'http://localhost:3000/api'
});

2. Create API Fetchers

import { createFetcher } from '@consolify/api';

// Define your data types
interface User {
  id: number;
  name: string;
  email: string;
}

interface CreateUserRequest {
  name: string;
  email: string;
}

// Create fetchers for different endpoints
const getUserFetcher = createFetcher<User>('/users/:id');
const getUsersFetcher = createFetcher<User[]>('/users');
const createUserFetcher = createFetcher<User, CreateUserRequest>('/users', 'POST');
const updateUserFetcher = createFetcher<User, Partial<User>>('/users/:id', 'PUT');
const deleteUserFetcher = createFetcher<void>('/users/:id', 'DELETE');

3. Use with react-query-kit Router

import { router } from '@consolify/api';

// Define your API routes using the router pattern
const userApi = router('users', {
  // GET /users
  list: router.query({
    fetcher: createFetcher<User[]>('/users')
  }),
  
  // GET /users/:id
  get: router.query({
    fetcher: createFetcher<User>('/users/:id')
  }),
  
  // POST /users
  create: router.mutation({
    fetcher: createFetcher<User, CreateUserRequest>('/users', 'POST')
  }),
  
  // PUT /users/:id
  update: router.mutation({
    fetcher: createFetcher<User, Partial<User>>('/users/:id', 'PUT')
  }),
  
  // DELETE /users/:id
  delete: router.mutation({
    fetcher: createFetcher<void>('/users/:id', 'DELETE')
  })
});

// Use in components
function UserProfile({ userId }: { userId: number }) {
  const { data: user, isLoading } = userApi.get.useQuery({
    variables: { id: userId }
  });

  if (isLoading) return <div>Loading...</div>;
  return <div>{user?.name}</div>;
}

function UsersList() {
  const { data: users } = userApi.list.useQuery();
  const createUser = userApi.create.useMutation();
  const updateUser = userApi.update.useMutation();
  const deleteUser = userApi.delete.useMutation();

  const handleCreate = (userData: CreateUserRequest) => {
    createUser.mutate(userData);
  };

  const handleUpdate = (id: number, userData: Partial<User>) => {
    updateUser.mutate({ id, ...userData });
  };

  const handleDelete = (id: number) => {
    deleteUser.mutate({ id });
  };

  // ... component implementation
}

Alternative: Direct React Query Usage

import { useQuery, useMutation } from '@tanstack/react-query';

// GET request with URL parameters
function UserProfile({ userId }: { userId: number }) {
  const { data: user, isLoading } = useQuery({
    queryKey: ['user', userId],
    queryFn: ({ queryKey }) => getUserFetcher({ id: queryKey[1] }, {} as any),
  });

  if (isLoading) return <div>Loading...</div>;
  return <div>{user?.name}</div>;
}

// POST request
function CreateUserForm() {
  const createUser = useMutation({
    mutationFn: (userData: CreateUserRequest) =>
      createUserFetcher(userData, {} as any),
  });

  const handleSubmit = (userData: CreateUserRequest) => {
    createUser.mutate(userData);
  };

  // ... form implementation
}

API Reference

configureApi(config)

Configure the API client with global settings.

Parameters:

  • config.baseUrl (string): The base URL for all API requests

Example:

configureApi({
  baseUrl: 'https://api.example.com'
});

getApiConfig()

Get the current API configuration.

Returns: Current configuration object

Example:

const config = getApiConfig();
console.log(config.baseUrl); // 'https://api.example.com'

createFetcher<TFnData, TVariables, TPageParam>(url, method?, options?)

Create a type-safe fetcher function for API endpoints.

Type Parameters:

  • TFnData: The expected response data type
  • TVariables: The request variables/payload type (default: Record<string, any>)
  • TPageParam: The pagination parameter type (default: never)

Parameters:

  • url (string): The endpoint URL (can include :param placeholders)
  • method (string, optional): HTTP method (default: 'GET')
  • options (RequestInit, optional): Additional fetch options (excluding method)

Returns: A fetcher function compatible with TanStack React Query

URL Parameter Replacement

The package automatically replaces URL parameters using the :param syntax:

const getUserFetcher = createFetcher<User>('/users/:id/posts/:postId');

// Usage
const { data } = useQuery({
  queryKey: ['user-posts', userId, postId],
  queryFn: getUserFetcher,
  variables: { 
    id: userId,      // Replaces :id in URL
    postId: postId   // Replaces :postId in URL
  },
});

HTTP Methods

GET Requests

const getFetcher = createFetcher<ResponseType>('/endpoint');
// or explicitly
const getFetcher = createFetcher<ResponseType>('/endpoint', 'GET');

POST Requests

const postFetcher = createFetcher<ResponseType, RequestType>('/endpoint', 'POST');

PUT/PATCH Requests

const putFetcher = createFetcher<ResponseType, RequestType>('/endpoint/:id', 'PUT');
const patchFetcher = createFetcher<ResponseType, Partial<RequestType>>('/endpoint/:id', 'PATCH');

DELETE Requests

const deleteFetcher = createFetcher<void>('/endpoint/:id', 'DELETE');

Request Body Handling

The package automatically handles different types of request bodies:

JSON Data

const createUserFetcher = createFetcher<User, CreateUserRequest>('/users', 'POST');

// Automatically serialized as JSON
createUser.mutate({
  name: 'John Doe',
  email: '[email protected]'
});

FormData (File Uploads)

const uploadFetcher = createFetcher<UploadResponse, FormData>('/upload', 'POST');

const formData = new FormData();
formData.append('file', file);
formData.append('description', 'My file');

upload.mutate(formData); // Sent as multipart/form-data

Custom Headers and Options

const fetcher = createFetcher<ResponseType>('/endpoint', 'POST', {
  headers: {
    'Authorization': 'Bearer token',
    'X-Custom-Header': 'value'
  },
  credentials: 'include',
  cache: 'no-cache'
});

Error Handling

Errors are automatically propagated and can be handled using React Query's error handling:

const { data, error, isError } = useQuery({
  queryKey: ['users'],
  queryFn: getUsersFetcher,
  onError: (error) => {
    console.error('Failed to fetch users:', error);
  }
});

if (isError) {
  return <div>Error: {error.message}</div>;
}

Advanced Usage

Complex API Structure with Nested Routes

import { router } from '@consolify/api';

// Organize your API with nested structures
const api = router('api', {
  users: router('users', {
    list: router.query({
      fetcher: createFetcher<User[]>('/users')
    }),
    get: router.query({
      fetcher: createFetcher<User>('/users/:id')
    }),
    posts: router('posts', {
      list: router.query({
        fetcher: createFetcher<Post[]>('/users/:userId/posts')
      }),
      get: router.query({
        fetcher: createFetcher<Post>('/users/:userId/posts/:postId')
      }),
      create: router.mutation({
        fetcher: createFetcher<Post, CreatePostRequest>('/users/:userId/posts', 'POST')
      })
    })
  }),
  
  auth: router('auth', {
    login: router.mutation({
      fetcher: createFetcher<AuthResponse, LoginRequest>('/auth/login', 'POST')
    }),
    logout: router.mutation({
      fetcher: createFetcher<void>('/auth/logout', 'POST')
    }),
    refresh: router.mutation({
      fetcher: createFetcher<AuthResponse>('/auth/refresh', 'POST')
    })
  })
});

// Usage
function UserPosts({ userId }: { userId: number }) {
  const { data: posts } = api.users.posts.list.useQuery({
    variables: { userId }
  });
  
  const createPost = api.users.posts.create.useMutation();
  
  const handleCreatePost = (postData: CreatePostRequest) => {
    createPost.mutate({ userId, ...postData });
  };
  
  // ... component implementation
}

Simple Router Pattern

import { router } from '@consolify/api';

// Simple API structure
const testApi = router('test', {
  get: router.query({
    fetcher: createFetcher<TestData>('/test')
  }),
  
  create: router.mutation({
    fetcher: createFetcher<TestData, CreateTestRequest>('/test', 'POST')
  }),
  
  update: router.mutation({
    fetcher: createFetcher<TestData, UpdateTestRequest>('/test/:id', 'PUT')
  })
});

// Usage in components
function TestComponent() {
  const { data: testData, isLoading } = testApi.get.useQuery();
  const createTest = testApi.create.useMutation();
  const updateTest = testApi.update.useMutation();
  
  if (isLoading) return <div>Loading...</div>;
  
  return (
    <div>
      <h1>{testData?.title}</h1>
      <button onClick={() => createTest.mutate({ title: 'New Test' })}>
        Create Test
      </button>
    </div>
  );
}

Multiple Base URLs

If you need different base URLs for different parts of your app, you can create separate fetcher instances:

// Configure main API
configureApi({ baseUrl: 'https://api.example.com' });

// For a specific endpoint that uses a different base URL
const externalFetcher = createFetcher<ExternalData>('https://external-api.com/data');

Dependencies

  • @tanstack/react-query: ^5.87.1
  • react-query-kit: ^3.3.2

License

MIT

Contributing

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