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

@tcnext/tcapi-client

v1.0.3

Published

`tcAPIClient` is a modular and extensible API client for TypeScript projects, built on top of Axios. It's designed to simplify interactions with REST APIs by providing a clean, intuitive interface for common operations, along with advanced features like c

Readme

tcAPI Client

tcAPIClient is a modular and extensible API client for TypeScript projects, built on top of Axios. It's designed to simplify interactions with REST APIs by providing a clean, intuitive interface for common operations, along with advanced features like collection-based CRUD, real-time event handling, and a query builder.

Features

  • Axios-Powered: Leverages the robustness and flexibility of Axios for HTTP requests.
  • Typed Collections: Provides a type-safe way to interact with API resources (collections).
  • CRUD Operations: Simplifies common Create, Read, Update, and Delete operations on collections.
  • Query Builder Integration: Allows for complex querying with filtering, sorting, and pagination. (Assumes a query-builder.ts utility is present).
  • Real-time Event System: Enables event-driven updates for API requests, allowing your application to react to data changes.
  • Direct HTTP Methods: Offers direct access to GET, POST, PATCH, PUT, DELETE, HEAD, and OPTIONS methods for more granular control.
  • Configurable: Supports global configuration for baseURL, authentication tokens, default pagination limits, and Axios-specific settings.
  • Request Logging: Optional logging of API calls for debugging purposes.

Installation

This client is intended to be used as part of a project. Ensure you have the necessary dependencies, primarily axios.

If you're using this in a project managed by Bun (as indicated by the original project setup):

bun install axios
# or if you prefer npm
# npm install axios
# or yarn
# yarn add axios

Getting Started

Initialization

First, import and instantiate the tcapiClient:

import { tcapiClient } from "./src/tcapi-client"; // Adjust path as needed

// Define an interface for your data (optional but recommended)
interface Article {
  id: string | number;
  title: string;
  content: string;
  // other fields...
}

const apiClient = new tcapiClient({
  baseURL: "https://api.example.com/v1", // Your API base URL
  auth: "YOUR_API_TOKEN", // Optional: Bearer token for authentication
  defaultPaginationLimit: 25, // Optional: Default limit for paginated collection queries
  logCalls: true, // Optional: Set to true to log API requests to the console
  axiosConfig: {
    // Optional: Any additional Axios request config defaults
    timeout: 5000
  }
});

Constructor Options (TcApiConstructorProps)

  • baseURL?: string: The base URL for all API requests.
  • auth?: string: An authentication token. If provided, it will be sent as a Bearer token in the Authorization header.
  • axiosConfig?: AxiosRequestConfig: Additional Axios configuration options to be applied to the underlying Axios instance.
  • defaultPaginationLimit?: number: Sets a default limit for pagination when using collection methods if not specified in the query.
  • logCalls?: boolean: If true, the client will log details (method and URL) of each API call to the console. Defaults to false.

Usage

1. Collection-based Operations

The collection<TItem>(collectionName: string) method provides a handler for a specific API resource (e.g., "articles", "users"). TItem is the expected type for items in that collection.

const articlesCollection = apiClient.collection<Article>("articles");

// --- Find multiple items ---
async function fetchAllArticles() {
  try {
    const response = await articlesCollection.find();
    // Default pagination limit (if set) will be applied here
    // response.data will be Article[]
    console.log("All articles:", response.data);
  } catch (error) {
    console.error("Error fetching articles:", error);
  }
}

// --- Find with Query Parameters (Sorting, Filtering, Pagination) ---
async function fetchRecentArticles() {
  try {
    const response = await articlesCollection.find({
      sort: [{ field: "createdAt", order: "desc" }],
      filters: [{ field: "status", operator: "$eq", value: "published" }],
      pagination: { limit: 5, offset: 0 } // Overrides defaultPaginationLimit
    });
    console.log("Recent articles:", response.data);
  } catch (error) {
    console.error("Error fetching recent articles:", error);
  }
}

// --- Find a single item by ID ---
async function fetchArticleById(id: string | number) {
  try {
    const response = await articlesCollection.findOne(id);
    // response.data will be Article
    console.log("Article:", response.data);
  } catch (error) {
    console.error(`Error fetching article ${id}:`, error);
  }
}

// --- Create a new item ---
async function createArticle(newArticleData: Partial<Article>) {
  try {
    const response = await articlesCollection.create(newArticleData);
    // response.data will be the created Article
    console.log("Created article:", response.data);
    return response.data;
  } catch (error) {
    console.error("Error creating article:", error);
  }
}

// --- Update an existing item ---
async function updateArticle(id: string | number, updates: Partial<Article>) {
  try {
    const response = await articlesCollection.update(id, updates);
    // response.data will be the updated Article
    console.log("Updated article:", response.data);
  } catch (error) {
    console.error(`Error updating article ${id}:`, error);
  }
}

// --- Delete an item ---
async function deleteArticle(id: string | number) {
  try {
    const response = await articlesCollection.delete(id);
    // response.data might be the deleted Article or a confirmation, depends on API
    console.log("Deleted article:", response.data);
  } catch (error) {
    console.error(`Error deleting article ${id}:`, error);
  }
}

2. Query Builder (QueryBuilderSettings)

Collection methods (find, findOne, create, update, delete) accept an optional queryParams object. This object is processed by the queryBuilder utility to construct the query string.

The structure of QueryBuilderSettings (defined in query-builder.ts) typically includes:

  • sort?: { field: string; order: "asc" | "desc" }[]: Sorting criteria.
  • filters?: { field: string; operator: string; value: any }[]: Filtering conditions.
  • pagination?: { limit?: number; offset?: number; page?: number; pageSize?: number }: Pagination parameters.
  • Other custom query parameters your API might support.

Note: The defaultPaginationLimit set during client initialization will be automatically applied if queryParams.pagination.limit is not provided in a collection method call.

3. Real-time Updates with Events

The client allows you to listen for events and re-fetch data when those events are emitted. This is useful for keeping your UI in sync with backend changes without manual polling.

Each request made through a CollectionHandler or a direct HTTP method (that returns TcApiRequest) has an .on() method.

const postsCollection = apiClient.collection<Article>("posts");

// 1. Initial fetch and register an event listener
postsCollection
  .find({ sort: [{ field: "date", order: "desc" }] })
  .on("postsUpdated", (response) => {
    // This callback is triggered when 'postsUpdated' event is emitted
    console.log("🔄 Posts updated (event):", response.data);
    // Update your UI or state here with response.data
  })
  .then((response) => {
    // This is the response from the initial .find() call
    console.log("📰 Initial posts:", response.data);
    // Populate your UI or state with initial data
  })
  .catch((error) => {
    console.error("❌ Error fetching posts:", error);
  });

// 2. Somewhere else in your application, when you know data has changed:
function someActionThatChangesPosts() {
  // After an action like creating or updating a post on the server...
  console.log("🚀 Emitting 'postsUpdated' event...");
  apiClient.emit("postsUpdated");
}

// Example: Trigger the event after 5 seconds
setTimeout(someActionThatChangesPosts, 5000);

How it works:

  • When you call .on("eventName", callback), the original request function is registered with the client under "eventName".
  • The initial request executes as usual.
  • When client.emit("eventName") is called, the client re-executes the registered request function.
  • The onSuccess handler (which wraps your eventCallback) is then called with the new data, and the promise chain associated with the .on() call is resolved/rejected.

4. Direct HTTP Methods

For requests that don't fit the collection model or for more direct control, you can use Axios-like methods directly on the client instance. These methods also return a TcApiRequest object, so they support the .on() event mechanism.

// --- GET Request ---
async function fetchUserData(userId: string) {
  try {
    const response = await apiClient.get<{ id: string; name: string }>(
      `/users/${userId}`
    );
    console.log("User data:", response.data);
  } catch (error) {
    console.error("Error fetching user data:", error);
  }
}

// --- POST Request ---
async function createUser(userData: { name: string; email: string }) {
  try {
    const response = await apiClient.post<{ id: string; createdAt: string }>(
      "/users",
      userData
    );
    console.log("Created user:", response.data);
  } catch (error) {
    console.error("Error creating user:", error);
  }
}

// --- PATCH Request ---
async function updateUserProfile(
  userId: string,
  profileData: { bio?: string }
) {
  try {
    const response = await apiClient.patch(`/users/${userId}`, profileData);
    console.log("Updated profile:", response.data);
  } catch (error) {
    console.error("Error updating profile:", error);
  }
}

// --- PUT Request --- (Note: PATCH is generally preferred for partial updates)
async function replaceUser(
  userId: string,
  fullUserData: { name: string; email: string }
) {
  try {
    const response = await apiClient.put(`/users/${userId}`, fullUserData);
    console.log("Replaced user:", response.data);
  } catch (error) {
    console.error("Error replacing user:", error);
  }
}

// --- DELETE Request ---
async function deleteUser(userId: string) {
  try {
    const response = await apiClient.delete(`/users/${userId}`);
    console.log("Delete response:", response.data); // Or check response.status
  } catch (error) {
    console.error("Error deleting user:", error);
  }
}

// --- HEAD Request ---
async function checkResourceHeaders(url: string) {
  try {
    const response = await apiClient.head(url); // Returns AxiosResponse, not TcApiRequest
    console.log("Headers:", response.headers);
  } catch (error) {
    console.error("Error with HEAD request:", error);
  }
}

// --- OPTIONS Request ---
async function checkSupportedMethods(url: string) {
  try {
    const response = await apiClient.options(url); // Returns AxiosResponse, not TcApiRequest
    console.log("Allowed methods:", response.headers.allow);
  } catch (error) {
    console.error("Error with OPTIONS request:", error);
  }
}

// --- Custom Request ---
async function makeCustomRequest() {
  try {
    const response = await apiClient.request({
      // Returns AxiosResponse, not TcApiRequest
      method: "GET",
      url: "/some-custom-endpoint",
      params: {
        customParam: "value"
      }
    });
    console.log("Custom request response:", response.data);
  } catch (error) {
    console.error("Error with custom request:", error);
  }
}

Note on TcApiRequest: Most methods (get, post, patch, put, delete from the client, and all CollectionHandler methods) return a TcApiRequest. This is an extended Promise that includes the .on() method for event handling. head, options, and request methods return a standard Axios Promise<AxiosResponse>.

5. Accessing the Axios Instance

If you need to interact with the underlying Axios instance directly (e.g., to add interceptors dynamically after client initialization or use features not exposed by tcapiClient):

const axiosInstance = apiClient.getInstance();

// Example: Add a response interceptor
axiosInstance.interceptors.response.use(
  (response) => {
    // Any status code that lie within the range of 2xx cause this function to trigger
    return response;
  },
  (error) => {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    console.error("Global Axios error interceptor:", error.message);
    return Promise.reject(error);
  }
);

This README.md provides a comprehensive guide to using the tcapiClient. Remember to adapt paths and type definitions (Article in examples) to your specific project structure and data models.