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

ask-core

v1.0.13

Published

Axios Service Kit

Readme

🧰 ASK — Axios Service Kit

A lightweight, typed, extensible service layer built on Axios. Designed to simplify API client creation with auth, error mapping, and clean service abstractions.

:warning Ask core is not yet stable as it is brand new. I am already using it in many project so I intend to keep it maintained on the long term.

🚀 Features

  • Typed service classes for GET/POST/PUT/PATCH/DELETE
  • Built-in request interceptor with optional auth token injection
  • Extensible error mapping (map HTTP status → custom errors)
  • Simple, predictable API (ReadOnlyService, ModelService)
  • Reusable Axios instance per service
  • Minimal boilerplate for API layers

Installation

npm install ask-core
# or
yarn add ask-core
# or
pnpm add ask-core

🛠️ Usage

ASK is built around three core classes:

  • BaseApiService — Axios wrapper with auth + error mapping
  • ReadOnlyApiService — GET-only service
  • ModelApiService — GET + POST + PUT + PATCH + DELETE

FULL CODE EXAMPLE AVAILABLE HERE

1. Initialize the AskClient

createAskClient helps you to create your client but also two utility classes that will use to define your services.

  • ModelService utility class exposes get, post ,delete, patch, put for you to define your services
  • ReadOnlyService is the same class except that it only exposes the get method.

See below how to use these utility class to use create a new ApiService

//src/ask.ts
import { createAskClient } from "ask-core";

// Any method to get the access token
const getToken = async () => sessionStorage.getItem('API_TOKEN')!

// Initialize client
const { client: baseClient, ModelService, ReadOnlyService } = createAskClient(
  "https://api.example.com",
  { getToken, authHeader: 'Bearer'}
);

export { baseClient, ModelService, ReadOnlyService };

2. Write a Service

Writing your service can be done anywhere in your directory tree. I usually like to keep it next to my features

//src/features/todos/api.ts
import { ModelService} from '@/ask'

export default class TodoApiService extends ModelService {
  constructor() {
    super({ requiresAuth:false }); // will add the Bearer token 
  }
  async getById(todoId:string) {
    return this.get<Todo>({url: `/todos/${todoId}`})
  }
  async getAll() {
    return this.get({url: `/todos/`})
  }
}

3. Register your service

Create a new file ask-services.ts next to your ask.ts file. In this file we will register all your services in a one place.

Registering your service is important so the api is aware of all services registered. You can do this:

  • Eager imports by calling the registerServices
  • Lazy imports by calling the registerLazyServices
//src/ask-services.ts
import { baseClient } from "./ask";
import TodosApiService from "./features/todos/api";

// 👇 You MUST reuse the returned client for better typing support
const client = baseClient
  // Eager services (bundle size might increase if you have big services)
  .registerServices({
    todos: TodosApiService,
  })
  // LazyService to have lazy imports and to avoid circular imports
  .registerLazyServices({
    // Lazy services 🎉  Preferred !
    // todos: () => import("@/features/dogs/api"),
    projects: () => import("@/features/projects/api"),
    // etc..
  });

// Rename your export to have to only use 'api' afterwards
export const { services: api } = client;

Now all your services are registered in one place. It is very easy to manage

4. Use the service layer

Once registered the services are available on the client. You can simply find all your registered services with their associated keyword.

import { api } from '@/ask-services'
const todos = await api.todos.getAll()  // eager
const projects = await api.projects.list() // lazy
// etc...
//

🎉 Congratulation, you created your first API Service layer

Authentication

Service-level default:

You can mark a whole service as private by passing requiresAuth: true in the constructor:

super({ errorMap, requiresAuth: true });

Request-level override:

Individual requests can override via config:

this.get({ url: "/some-public-endpoint", config: { requiresAuth: false } });

Here is an full example

class TodoApiService extends ModelApiClass {
  constructor() {
    super({ requiresAuth: true }); // all requests require auth by default
  }
  async getTodos() {
    // Request override
   return this.get<Todo[]>({ url: "/public-endpoint", config: { requiresAuth: false } });
  }
}

[!NOTE] You never have to manually attach Authorization headers — the interceptor automatically handles it based on requiresAuth.

🛡️ Error Mapping

ASK lets you map HTTP status codes to:

  • custom error classes
  • or custom factory functions
import { ModelService } from "@/ask";

class NotFoundError extends Error {}
class UnauthorizedError extends Error {}

const errorMap = {
  401: UnauthorizedError,
  404: NotFoundError,
  500: (err) => new Error("Server exploded: " + err.message),
};

export class WorkspaceApiService extends ModelService {
  constructor() {
    super({ errorMap });
  }
}

Use it with your favourite library

ReactQuery Example

// src/features/todos/hooks.ts
import { useQuery } from "@tanstack/react-query";
import { api } from "@/ask-services";


export const useGetAllTodos = () => {
  const query = useQuery({queryKey: ["todos"], queryFn: () => api.todos.getAll()});
  return query
};

export const useGetTodobyId = (todoId:string) => {
  const query = useQuery({queryKey: ["todos", todoId], queryFn: () => api.todos.getById(todoId)});
  return query
};

🤝 Contributing

Contributions, issues, and feature requests are welcome! Feel free to open a PR or GitHub issue.

📄 License

MIT License © 2025