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

@honestjs/rpc-plugin

v1.1.0

Published

RPC plugin for HonestJS framework

Readme

@honestjs/rpc-plugin

A comprehensive RPC plugin for HonestJS that combines route analysis, schema generation, and client generation into a single solution.

Features

  • Route Analysis: Automatically analyzes controller methods and extracts type information using ts-morph
  • Schema Generation: Generates JSON schemas and TypeScript interfaces from types used in controllers
  • Client Generation: Creates a fully-typed TypeScript RPC client with proper parameter typing
  • Type Safety: Full TypeScript support with generated types and interfaces

Installation

npm install @honestjs/rpc-plugin
# or
yarn add @honestjs/rpc-plugin
# or
pnpm add @honestjs/rpc-plugin

Usage

Basic Setup

import { RPCPlugin } from '@honestjs/rpc-plugin'
import { Application } from 'honestjs'

const app = new Application({
	plugins: [
		new RPCPlugin({
			outputDir: './generated/rpc'
		})
	]
})

Configuration Options

interface RPCPluginOptions {
	readonly controllerPattern?: string // Glob pattern for controller files (default: 'src/modules/*/*.controller.ts')
	readonly tsConfigPath?: string // Path to tsconfig.json (default: 'tsconfig.json')
	readonly outputDir?: string // Output directory for generated files (default: './generated/rpc')
	readonly generateOnInit?: boolean // Generate files on initialization (default: true)
}

What It Generates

TypeScript RPC Client (client.ts)

The plugin generates a single comprehensive file that includes both the client and all type definitions:

  • Controller-based organization: Methods grouped by controller
  • Type-safe parameters: Path, query, and body parameters with proper typing
  • Flexible request options: Clean separation of params, query, body, and headers
  • Error handling: Built-in error handling with custom ApiError class
  • Header management: Easy custom header management
  • Custom fetch support: Inject custom fetch implementations for testing, middleware, and compatibility
  • Integrated types: All DTOs, interfaces, and utility types included in the same file
// Generated client usage
import { ApiClient } from './generated/rpc/client'

// Create client instance with base URL
const apiClient = new ApiClient('http://localhost:3000')

// Type-safe API calls
const user = await apiClient.users.create({
	body: { name: 'John', email: '[email protected]' }
})

const users = await apiClient.users.list({
	query: { page: 1, limit: 10 }
})

const user = await apiClient.users.getById({
	params: { id: '123' }
})

// Set custom headers
apiClient.setDefaultHeaders({
	'X-API-Key': 'your-api-key',
	Authorization: 'Bearer your-jwt-token'
})

// Use with custom fetch function (e.g., for testing or custom logic)
const customFetch = (input: RequestInfo | URL, init?: RequestInit) => {
	console.log('Making request to:', input)
	return fetch(input, init)
}

const apiClientWithCustomFetch = new ApiClient('http://localhost:3000', {
	fetchFn: customFetch,
	defaultHeaders: { 'X-Custom-Header': 'value' }
})

The generated client.ts file contains everything you need:

  • ApiClient class with all your controller methods
  • Type definitions for requests, responses, and DTOs
  • Utility types like RequestOptions and ApiResponse
  • Generated interfaces from your controller types

Custom Fetch Functions

The RPC client supports custom fetch implementations, which is useful for:

  • Testing: Inject mock fetch functions for unit testing
  • Custom Logic: Add logging, retries, or other middleware
  • Environment Compatibility: Use different fetch implementations (node-fetch, undici, etc.)
  • Interceptors: Wrap requests with custom logic before/after execution

Basic Custom Fetch Example

// Simple logging wrapper
const loggingFetch = (input: RequestInfo | URL, init?: RequestInit) => {
	console.log(`[${new Date().toISOString()}] Making ${init?.method || 'GET'} request to:`, input)
	return fetch(input, init)
}

const apiClient = new ApiClient('http://localhost:3000', {
	fetchFn: loggingFetch
})

Advanced Custom Fetch Examples

// Retry logic with exponential backoff
const retryFetch = (maxRetries = 3) => {
	return async (input: RequestInfo | URL, init?: RequestInit) => {
		for (let i = 0; i <= maxRetries; i++) {
			try {
				const response = await fetch(input, init)
				if (response.ok) return response

				if (i === maxRetries) return response

				// Wait with exponential backoff
				await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000))
			} catch (error) {
				if (i === maxRetries) throw error
			}
		}
		throw new Error('Max retries exceeded')
	}
}

const apiClientWithRetry = new ApiClient('http://localhost:3000', {
	fetchFn: retryFetch(3)
})

// Request/response interceptor
const interceptorFetch = (input: RequestInfo | URL, init?: RequestInit) => {
	// Pre-request logic
	const enhancedInit = {
		...init,
		headers: {
			...init?.headers,
			'X-Request-ID': crypto.randomUUID()
		}
	}

	return fetch(input, enhancedInit).then((response) => {
		// Post-response logic
		console.log(`Response status: ${response.status}`)
		return response
	})
}

const apiClientWithInterceptor = new ApiClient('http://localhost:3000', {
	fetchFn: interceptorFetch
})

Testing with Custom Fetch

// Mock fetch for testing
const mockFetch = jest.fn().mockResolvedValue({
	ok: true,
	json: () => Promise.resolve({ data: { id: '123', name: 'Test User' } })
})

const testApiClient = new ApiClient('http://test.com', {
	fetchFn: mockFetch
})

// Your test can now verify the mock was called
expect(mockFetch).toHaveBeenCalledWith('http://test.com/api/v1/users/123', expect.objectContaining({ method: 'GET' }))

How It Works

1. Route Analysis

  • Scans your HonestJS route registry
  • Uses ts-morph to analyze controller source code
  • Extracts method signatures, parameter types, and return types
  • Builds comprehensive route metadata

2. Schema Generation

  • Analyzes types used in controller methods
  • Generates JSON schemas using ts-json-schema-generator
  • Creates TypeScript interfaces from schemas
  • Integrates with route analysis for complete type coverage

3. Client Generation

  • Groups routes by controller for organization
  • Generates type-safe method signatures
  • Creates parameter validation and typing
  • Builds the complete RPC client with proper error handling

Benefits of the Unified Approach

  • No Duplication: Single source of truth for all type information
  • Tight Coupling: Components share data directly without file I/O
  • Better Performance: Eliminates redundant analysis and file generation
  • Consistent Types: All generated code uses the same type definitions
  • Easier Maintenance: Single plugin to configure and maintain

Example Generated Output

Generated Client

export class ApiClient {
	get users() {
		return {
			create: async (
				options: RequestOptions<{ name: string; email: string }, undefined, undefined, undefined>
			): Promise<ApiResponse<any>> => {
				return this.request('POST', `/api/v1/users/`, options)
			},
			list: async (
				options?: RequestOptions<undefined, { page: number; limit: number }, undefined, undefined>
			): Promise<ApiResponse<any>> => {
				return this.request('GET', `/api/v1/users/`, options)
			}
		}
	}
}

// RequestOptions type definition
export type RequestOptions<
	TParams = undefined,
	TQuery = undefined,
	TBody = undefined,
	THeaders = undefined
> = (TParams extends undefined ? object : { params: TParams }) &
	(TQuery extends undefined ? object : { query: TQuery }) &
	(TBody extends undefined ? object : { body: TBody }) &
	(THeaders extends undefined ? object : { headers: THeaders })

Plugin Lifecycle

The plugin automatically generates files when your HonestJS application starts up (if generateOnInit is true). You can also manually trigger generation:

const rpcPlugin = new RPCPlugin()
await rpcPlugin.analyze() // Manually trigger analysis and generation

Dependencies

  • ts-morph: TypeScript source code analysis
  • ts-json-schema-generator: JSON schema generation from TypeScript types
  • honestjs: Core framework integration

License

MIT