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

@di-framework/rpc

v5.3.7

Published

Decorator-centric JSON-RPC and per-method gRPC for di-framework

Readme

@di-framework/rpc

Decorator-centric JSON-RPC 2.0 and per-method gRPC for @di-framework/core. Decorators are the schema: the same service runs over memory, HTTP, sockets, Connect, gRPC-Web, and native gRPC without application .proto generation.

Install

bun add @di-framework/rpc @di-framework/core

For gRPC:

bun add @connectrpc/connect @connectrpc/connect-node @bufbuild/protobuf

For sockets:

bun add @di-framework/socket

Define messages and a service

import {
  RpcField,
  RpcMessage,
  RpcMethod,
  RpcNotify,
  RpcService,
} from '@di-framework/rpc';

@RpcMessage()
class GetUserRequest {
  // Numeric shorthand defaults to a string field.
  @RpcField(1)
  id!: string;
}

@RpcMessage()
class User {
  @RpcField(1)
  id!: string;

  @RpcField(2)
  name!: string;

  @RpcField({ number: 3, type: 'int32' })
  loginCount!: number;
}

@RpcService({ package: 'example.v1' })
class UserService {
  @RpcMethod({ input: () => GetUserRequest, output: () => User })
  get(request: GetUserRequest): User {
    return { id: request.id, name: 'Ada', loginCount: 1 };
  }

  @RpcNotify({ input: () => GetUserRequest })
  touched(request: GetUserRequest): void {
    console.log(request.id);
  }
}

get becomes the first-class protobuf RPC /example.v1.UserService/Get and the JSON-RPC method example.v1.UserService/Get.

Nested and repeated messages use an explicit factory because the package does not require emitDecoratorMetadata:

@RpcField({ number: 4, type: () => Address, repeated: true })
addresses!: Address[];

Supported v1 scalar types are string, bool, int32, int64, double, and bytes.

Typed client and memory transport

import {
  createRpcClient,
  createRpcServer,
  memoryPair,
} from '@di-framework/rpc';

const { clientTransport, serverTransport } = memoryPair();
const server = createRpcServer({ transport: serverTransport });
await server.start();

// Passing the decorated class supplies the runtime service path while the
// returned proxy preserves method arguments and results.
const users = createRpcClient(UserService, clientTransport);
const user = await users.get({ id: '1' });
await users.touched({ id: '1' });

// Batch multiple calls into one JSON-RPC array:
const [a, b] = await users.$batch((rpc) => [
  rpc.get({ id: '1' }),
  rpc.get({ id: '2' }),
]);

// Abort / timeout a single call:
const ac = new AbortController();
await users.get({ id: '1' }, { signal: ac.signal, timeoutMs: 5_000 });

Client and server interceptors wrap each call (useful for auth, logging, deadlines):

const users = createRpcClient(UserService, clientTransport, {
  interceptors: [
    async (ctx, next) => {
      console.log(ctx.method);
      return next();
    },
  ],
});

createRpcServer({
  transport,
  interceptors: [
    async (ctx, next) => next(),
  ],
});

Throw RpcAppError from a method to control JSON-RPC and Connect codes:

import { RpcAppError, RPC_CONNECT_CODES } from '@di-framework/rpc';

throw new RpcAppError('missing user', {
  connectCode: RPC_CONNECT_CODES.NOT_FOUND,
  data: { id },
});

Type-only construction cannot discover a service path after TypeScript erases generics. When the class is unavailable, provide it explicitly:

const users = createRpcClient<UserService>(transport, {
  service: 'example.v1.UserService',
});

HTTP

import { createHttpRpcHandler, httpTransport } from '@di-framework/rpc/http';
import { createRpcClient } from '@di-framework/rpc';

const rpc = createHttpRpcHandler({ path: '/rpc' });
Bun.serve({ fetch: rpc });

const users = createRpcClient(
  UserService,
  httpTransport({ url: 'http://localhost:3000/rpc' }),
);

The HTTP endpoint supports JSON-RPC batches, notifications, and standard error codes.

Per-method gRPC / Connect

import { createServer } from 'node:http2';
import { createGrpcHandler, grpcTransport } from '@di-framework/rpc/grpc';
import { createRpcClient } from '@di-framework/rpc';

const handler = createGrpcHandler();
createServer(handler).listen(3000);

const users = createRpcClient(
  UserService,
  grpcTransport({ baseUrl: 'http://localhost:3000' }),
);

The adapter builds protobuf-es descriptors at runtime. Every @RpcMethod is a real unary service method served by Connect, gRPC-Web, and native gRPC. It is not a generic JSON envelope.

Inspect or export the generated IDL:

import { printProto } from '@di-framework/rpc';

console.log(printProto());

Socket

Adapt an established secure or plain @di-framework/socket connection:

import { socketTransport } from '@di-framework/rpc/socket';

const users = createRpcClient(UserService, socketTransport(connection));

Lifecycle

Pass a transport to @RpcService to auto-start when DI resolves the service, or bootstrap all services on one transport:

await startRpcServices({ transport });
await stopRpcServices();