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

@honorestjs/client

v0.1.0

Published

Type-safe HTTP client for HonorestJS contracts - Automatically generated API client with full TypeScript support

Downloads

9

Readme

@honorestjs/client

Type-safe HTTP client for HonorestJS contracts

npm version License: MIT

@honorestjs/client provides an automatically generated, type-safe HTTP client for your API contracts. Built on top of @honorestjs/contract, it ensures complete type safety from server to client with zero code generation.

Features

🎯 Fully Type-Safe - Complete TypeScript inference for inputs and outputs
🚀 Auto-Generated API - Client methods generated from contracts
🔒 Type-Safe Errors - Typed error handling based on contract error schemas
Fetch-Based - Uses native fetch API (works in browsers and Node.js 18+)
🎣 Interceptors - Request/response/error interceptors
📦 Lightweight - Minimal dependencies, tree-shakeable

Installation

npm install @honorestjs/client @honorestjs/contract
# or
yarn add @honorestjs/client @honorestjs/contract
# or
pnpm add @honorestjs/client @honorestjs/contract
# or
bun add @honorestjs/client @honorestjs/contract

Quick Start

1. Define Your Contract

First, create your API contract using @honorestjs/contract:

// contracts/users.contract.ts
import { defineContract, endpoint, defineApp } from '@honorestjs/contract'
import { z } from 'zod'

const UsersContract = defineContract({
  name: 'users',
  path: '/users',
  endpoints: {
    getUser: endpoint({
      method: 'GET',
      path: '/:id',
      params: z.object({ id: z.string().uuid() }),
      output: z.object({
        id: z.string().uuid(),
        name: z.string(),
        email: z.string().email()
      }),
      errors: {
        404: z.object({ message: z.string() })
      }
    }),
    
    createUser: endpoint({
      method: 'POST',
      path: '/',
      body: z.object({
        name: z.string().min(1),
        email: z.string().email()
      }),
      output: z.object({
        id: z.string().uuid(),
        name: z.string(),
        email: z.string().email()
      })
    })
  }
})

export const AppContract = defineApp({
  prefix: '/api',
  contracts: {
    users: UsersContract
  }
})

2. Create the Client

import { createClient } from '@honorestjs/client'
import { AppContract } from './contracts'

const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000'
})

3. Make Type-Safe API Calls

// GET request with path parameters
const user = await api.users.getUser({ params: { id: '123' }})
//    ^? { id: string, name: string, email: string }

// POST request with body
const newUser = await api.users.createUser({
  body: { name: 'John Doe', email: '[email protected]' }
})
//    ^? { id: string, name: string, email: string }

API Reference

createClient(app, config)

Creates a type-safe HTTP client from an app contract.

const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000',
  headers: {
    'Authorization': 'Bearer token'
  },
  fetch: customFetch, // Optional custom fetch implementation
  onRequest: async (url, init) => {
    console.log('Request:', url)
  },
  onResponse: async (response) => {
    console.log('Response:', response.status)
  },
  onError: async (error) => {
    console.error('Error:', error)
  }
})

Config Options

| Option | Type | Description | |--------|------|-------------| | baseUrl | string | Required. Base URL for the API | | headers | Record<string, string> | Default headers for all requests | | fetch | typeof fetch | Custom fetch implementation (useful for testing) | | onRequest | (url, init) => void \| Promise<void> | Request interceptor | | onResponse | (response) => void \| Promise<void> | Response interceptor | | onError | (error) => void \| Promise<void> | Error interceptor |

Error Handling

ClientError

Thrown when the API returns an error response (4xx or 5xx).

import { ClientError } from '@honorestjs/client'

try {
  await api.users.getUser({ params: { id: 'invalid' }})
} catch (error) {
  if (error instanceof ClientError) {
    console.log(error.status)    // 404
    console.log(error.data)      // { message: "User not found" }
    console.log(error.message)   // "HTTP 404: Not Found"
    console.log(error.request)   // { method: 'GET', url: '...', headers: {} }
    
    // Helper methods
    if (error.is(404)) {
      console.log('Not found!')
    }
    
    if (error.isClientError()) {
      console.log('4xx error')
    }
    
    if (error.isServerError()) {
      console.log('5xx error')
    }
  }
}

NetworkError

Thrown when the request fails before reaching the server (network issues, CORS, etc.).

import { NetworkError } from '@honorestjs/client'

try {
  await api.users.getUser({ params: { id: '123' }})
} catch (error) {
  if (error instanceof NetworkError) {
    console.log('Network error:', error.message)
    console.log('Cause:', error.cause)
  }
}

ValidationError

Thrown when response doesn't match the contract (if validation is enabled).

import { ValidationError } from '@honorestjs/client'

try {
  await api.users.getUser({ params: { id: '123' }})
} catch (error) {
  if (error instanceof ValidationError) {
    console.log('Validation error:', error.message)
    console.log('Errors:', error.errors)
  }
}

Advanced Usage

Custom Headers Per Request

const user = await api.users.getUser({
  params: { id: '123' },
  headers: {
    'X-Custom-Header': 'value'
  }
})

Query Parameters

// Endpoint definition
listUsers: endpoint({
  method: 'GET',
  path: '/',
  query: z.object({
    page: z.number().default(1),
    limit: z.number().default(20),
    search: z.string().optional()
  }),
  output: z.object({
    users: z.array(UserSchema),
    total: z.number()
  })
})

// Client usage
const result = await api.users.listUsers({
  query: {
    page: 2,
    limit: 50,
    search: 'john'
  }
})

Authentication

// Global auth token
const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})

// Or use request interceptor for dynamic tokens
const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000',
  onRequest: async (url, init) => {
    const token = await getAuthToken()
    init.headers = {
      ...init.headers,
      'Authorization': `Bearer ${token}`
    }
  }
})

Request/Response Logging

const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000',
  onRequest: async (url, init) => {
    console.log(`→ ${init.method} ${url}`)
  },
  onResponse: async (response) => {
    console.log(`← ${response.status}`)
  },
  onError: async (error) => {
    console.error('✗ Request failed:', error.message)
  }
})

Custom Fetch Implementation

Useful for testing or adding custom behavior:

const api = createClient(AppContract, {
  baseUrl: 'http://localhost:3000',
  fetch: async (url, init) => {
    // Custom fetch logic
    console.log('Custom fetch:', url)
    return fetch(url, init)
  }
})

Type Inference

The client provides complete type inference:

import type { ClientInput, ClientOutput } from '@honorestjs/client'

// Get input type for an endpoint
type GetUserInput = ClientInput<typeof UsersContract.endpoints.getUser>
// { params: { id: string } }

// Get output type for an endpoint
type GetUserOutput = ClientOutput<typeof UsersContract.endpoints.getUser>
// { id: string, name: string, email: string }

Best Practices

1. Create a Shared API Instance

// api.ts
import { createClient } from '@honorestjs/client'
import { AppContract } from '@myapp/api-contract'

export const api = createClient(AppContract, {
  baseUrl: process.env.API_URL || 'http://localhost:3000',
  headers: {
    'Content-Type': 'application/json'
  }
})

2. Handle Errors Consistently

async function fetchUser(id: string) {
  try {
    return await api.users.getUser({ params: { id }})
  } catch (error) {
    if (error instanceof ClientError) {
      if (error.is(404)) {
        return null // User not found
      }
      throw new Error(`Failed to fetch user: ${error.message}`)
    }
    throw error
  }
}

3. Use TypeScript Strict Mode

{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true
  }
}

Troubleshooting

CORS Errors

If you're getting CORS errors, make sure your server allows requests from your client origin:

// Server-side (HonorestJS)
app.use('*', cors({
  origin: 'http://localhost:5173', // Your client URL
  credentials: true
}))

TypeScript Errors

Make sure you have the correct versions:

  • @honorestjs/contract: ^0.1.0
  • typescript: ^5.0.0

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

MIT © HonorestJS

Related Packages