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

infer-rpc

v1.0.2

Published

A library for statically typed and validated RPC clients and servers

Readme

InferRPC Introduction

InferRPC is a TypeScript library that facilitates writing API clients and servers that communicate by a protocol described by a shared schema. InferRPC provides the following benefits:

  1. Compile-time type checking of both client and server code.
  2. Run-time validation of requests and responses to ensure they comply with the schema's basic types as well as extended validation rule (e.g. does this string represent a URL?)
  3. Zero code generation.
  4. Easy integration into any backend framework. Currently, Koa and NextJS are supported.
  5. Extensible to support arbitrary serialization protocols (only JSON is currently supported).

InferRPC uses Zod for expressing request/response schemas and for validating payloads.

Usage

To implement an API client and/or server using InferRPC, you typically start by implementing a static data structure describing the schema. InferRPC uses type inference to infer the request/response types from this data structure.

InferRPC schemas are TypeScript dictionaries mapping method names to request and response schema.

In TypeScript, a schema is a data structure that adheres to the following type signature:

import * as z from "zod";

type AbstractSchemaType = Record<
  string,
  { req: z.ZodType<any>; res: z.ZodType<any> }
>;

Here's an example schema with 2 methods:

  1. sayHi, which takes a name parameter and returns a string.
  2. divide, which takes two numbers and returns the result of dividing num1 by num2.
import * as z from "zod";

export const testSchema = {
  sayHi: {
    req: z.object({
      name: z.string(),
    }),
    res: z.string(),
  },
  divide: {
    req: z.object({
      num1: z.number(),
      num2: z.number(),
    }),
    res: z.number(),
  },
};

This example only uses basic types (strings and numbers) but Zod lets you express more complex validation rules, such as whether a string contains a date, a URL, or an email address.

Server Example

A complete Koa-based server for this schema can be implemented with the following code snippet:

import Koa from "koa";
import Router from "koa-router";
import { Server } from "net";
import { createKoaRoute } from "InferRPC/koaAdapter";
import { ApiHttpError } from "InferRPC/types";
import { testSchema } from "./testSchema";

const createServer = (port: number): Server => {
  const koa = new Koa();
  const apiRouter = new Router({
    prefix: pathPrefix,
  });

  createKoaRoute(apiRouter, testSchema, "divide", async ({ num1, num2 }) => {
    if (num2 === 0) {
      throw new ApiHttpError("Can't divide by 0", 400);
    }
    return num1 / num2;
  });

  createKoaRoute(apiRouter, testSchema, "sayHi", async ({ name }) => {
    return "Hi " + name;
  });

  koa.use(apiRouter.allowedMethods());
  koa.use(apiRouter.routes());
  return koa.listen(port);
};

NextJS is also supported. This snippet shows to implement a NextJS API handler:

import { createNextHandler } from "InferRPC/nextAdapter"

export default createNextHandler(
      testSchema,
      "divide",
      async ({ num1, num2 }) => {
        if (num2 === 0) {
          throw new ApiHttpError("Can't divide by 0", 400);
        }
        return num1 / num2;
      }
    );
  });

Below are a few screenshots from VSCode highlighting the benefits of how InferRPC leverages TypeScript's type checking.

  • Your editor's auto-complete feature can help you choose a valid method name:
  • If you try to implement an invalid method name, you get an error:

  • If you try to add an invalid parameter name, you get an error:

  • If your method returns an invalid response type, you get an error:

Client Example

Implementing a client that adheres to the schema is easy. Here's an example:

const testClient = async () => {
  const client = new TypedHttpClient("http://localhost:3001/api", testSchema);
  const result = await client.call("divide", { num1: 10, num2: 2 });
  console.log(result);
};

The following screenshots show the benefits of InferRPC's static type checking.

  • Your editor can help you auto-complete the valid method names:

  • Auto-complete also works for parameters:

  • If you enter a wrong method name, you get an error:

  • If you enter a wrong parameter name, you get an error:

  • If you enter a wrong parameter type, you get an error:

  • The response type is checked as well:

  • If the client sends an invalid request to the server, the server will return a Zod validation error, as shown in this snippet (note that Zod lets you customize error messages if you want them to be more user friendly):

Peer-to-Peer Protocols

InferRPC also supports statically typed peer-to-peer protocols for usage with bi-directional streams such as WebSockets.

Peer-to-Peer protocols differ from client-server protocol in that they don't have a pre-defined request-response flow. Instead, each peer may send and receive messages at any time. This leads to a different schema definition:

type PeerSchema = Record<string, z.ZodType<any>>;

This schema is a mapping between method names and payloads, which can be any Zod type.

Here's an example, inspired by the client/server example above:

const schema1 = {
  divide: z.object({
    num1: z.number(),
    num2: z.number(),
  }),
  sayHi: z.object({
    name: z.string(),
  }),
};

const schema2 = {
  divideResult: z.number(),
  sayHiResult: z.string(),
};

To use InferRFC in a p2p application, create a Peer object. Its constructor takes 2 schemas: the schema for the incoming messages, the schema for the outgoing messages, and a listener for errors. Here's an example:

const listener: PeerListener = {
  onMissingHandler: (msgType) => {
    console.error("missing handler", msgType);
  },
  onParseError: (error) => {
    console.error("parse error", error);
  },
};

const peer1 = new Peer(schema1, schema2, listener);
const peer2 = new Peer(schema2, schema1, listener);

In this example, the two Peers are initialized. The Peers use opposite schemas for incoming and outgoing messages, but it's also possible for Peers to use the same schema if their protocol is symmetric.

The following snippet shows how to simulate bi-directional communication between peers:

peer1.setHandler("divide", async ({ num1, num2 }) => {
  peer2.onMessage(peer1.serialize("divideResult", num1 / num2));
});
peer1.setHandler("sayHi", async ({ name }) => {
  peer2.onMessage(peer1.serialize("sayHiResult", "Hi " + name));
});
peer2.setHandler("divideResult", async (num) => {
  console.log(num); // prints '2'
});
peer2.setHandler("sayHiResult", async (result) => {
  console.log(result); // prints "Hi Sarah";
});

peer1.onMessage(peer2.serialize("divide", { num1: 10, num2: 5 }));
peer1.onMessage(peer2.serialize("sayHi", { name: "Sarah" }));

Just as with the client/server examples above, InferRPC ensures types as statically checked and your IDE can auto-complete valid values for you.

  • Handler names are auto-completed:

  • So are handler parameters:

  • Invalid handler parameters cause compilation errors:

The same features work for serializing messages before they are sent over the wire.

  • Message names are auto-completed:

  • So are parameter names:

  • Invalid parameter types cause compilation errors: