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

defuss-rpc

v1.2.0

Published

Remote Procedure Call (RPC) for defuss.

Readme

Remote Procedure Call (RPC)

defuss-rpc is a tiny but powerful RPC library for building type-safe APIs in JavaScript and TypeScript. It enables seamless client-server communication with automatic type safety and minimal setup.

💡 Can you imagine? The whole codebase is written in only ~180 Lines of Code, providing full-featured RPC capabilities including type-safe client generation, server-side method routing, and automatic serialization using defuss-dson.

✨ Features

  • Type-safe: Full TypeScript support with automatic client type generation
  • Minimal setup: Just define your API classes and go
  • Automatic serialization: Uses defuss-dson for robust data serialization
  • Proxy-based client: Dynamic client generation with method interception
  • Schema introspection: Automatic API schema generation and discovery
  • Authentication and logging support: Built-in custom header and hook function support for access control, logging, performance monitoring and more
  • Framework agnostic: Works with Astro, Express.js, and any framework that supports Request/Response
  • Zero runtime dependencies: Tiny bundle size with minimal overhead

Getting Started

1. Install defuss-rpc:

bun install defuss-rpc

2. Define your API classes:

// <root>/service/FooApi.ts
export class FooApi {
  async getFoo(id: string) {
    // Your implementation here
    return { id, name: "Foo Item", data: "..." };
  }
  
  async createFoo(item: { name: string; data: string }) {
    // Your implementation here
    return { id: "new-id", ...item };
  }
}

// <root>/service/BarApi.ts
export class BarApi {
  async baz(value: string) {
    // Your implementation here
    return { result: `Processed: ${value}` };
  }
}

3. Set up the server and API registry:

Create the RPC API definition
// <root>/rpc.ts - Shared RPC API definition
import { createRpcServer, setGuardFunction } from "defuss-rpc/server";
import { FooApi } from "./service/FooApi.js";
import { BarApi } from "./service/BarApi.js";

// server-side RPC API definition
export const RpcApi = {
  FooApi,
  BarApi,
};

setGuardFunction(async (request) => {
  const payload = await request.json();
  console.log("Guard function called with request:", payload);
  // Implement your guard logic here
  return true;
});

createRpcServer(RpcApi); // expose the RPC API

// client-side RPC API type (for type safety)
export type RpcApi = typeof RpcApi;
Create the RPC route handler (Astro)
// pages/rpc/[...all].ts - Astro catch-all route handler
import { rpcRoute } from "defuss-rpc/server";
import "../../rpc.js"; // Import to register the RPC API

// Export the RPC route handler
export const POST = rpcRoute;
export const prerender = false;

4. Use on the client:

// <root>/component/some-component.ts
import { getRpcClient } from "defuss-rpc/client";
import type { RpcApi } from "../rpc.js";

// Get the RPC client with full type safety
// For Astro (default):
const rpc = await getRpcClient<RpcApi>();

// For Express.js or custom base URL:
// const rpc = await getRpcClient<RpcApi>("http://localhost:3000");

// Create API instances of the services, just as if we were on the server and use them
const fooApi = new rpc.FooApi();
const barApi = new rpc.BarApi();

// Call methods with full TypeScript support
const foo = await fooApi.getFoo("123");
const result = await barApi.baz("test value");

console.log(foo.name); // TypeScript knows this is a string
console.log(result.result); // Fully typed response

Advanced Features

Authentication & Guards

Protect your RPC endpoints with guard functions:

// <root>/rpc.ts - Set up guard function for authentication
import { setGuardFunction, type RpcCallDescriptor } from "defuss-rpc/server";

setGuardFunction(async (request: Request) => {
  const authHeader = request.headers.get("authorization");
  const call: RpcCallDescriptor = await request.json();

  // call.className, call.methodName, call.args can be used to check permissions
  console.log("Guard function called with request:", call);
  // Your authentication logic here - e.g. check JWT token, look up user in database and check for ACL
  return true; // Return true to allow access, false to deny (results in 403 Forbidden)
});

Schema Introspection

The RPC server automatically provides schema information:

import { getSchema } from "defuss-rpc/client";

const schema = await getSchema(); // btw. it caches the schema for the page lifetime
console.log(schema); // Array of class schemas with methods and properties
Alternative: Use with Express.js
// server.ts - Express.js server setup
import express from "express";
import { rpcRoute } from "defuss-rpc/server";
import "./rpc.js"; // Import to register the RPC API

const app = express();
const port = 3000;

// Parse JSON bodies
app.use(express.json());

// Add CORS headers
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
  if (req.method === "OPTIONS") {
    res.sendStatus(200);
  } else {
    next();
  }
});

// RPC endpoint handler
app.post("/rpc", async (req, res) => {
  try {
    // Convert Express request to standard Request object
    const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
    const request = new Request(url, {
      method: req.method,
      headers: req.headers as Record<string, string>,
      body: JSON.stringify(req.body),
    });

    // Use the defuss-rpc route handler
    const response = await rpcRoute({ request });
    
    // Send response back to Express
    const responseText = await response.text();
    res.status(response.status)
       .set("content-type", response.headers.get("content-type") || "application/json")
       .send(responseText);
  } catch (error) {
    console.error("RPC request error:", error);
    res.status(500).json({
      error: "Internal server error",
      message: error instanceof Error ? error.message : String(error),
    });
  }
});

// RPC schema endpoint
app.post("/rpc/schema", async (req, res) => {
  try {
    const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
    const request = new Request(url, {
      method: req.method,
      headers: req.headers as Record<string, string>,
      body: JSON.stringify(req.body),
    });

    const response = await rpcRoute({ request });
    const responseText = await response.text();
    res.status(response.status)
       .set("content-type", response.headers.get("content-type") || "application/json")
       .send(responseText);
  } catch (error) {
    console.error("RPC schema error:", error);
    res.status(500).json({
      error: "Internal server error",
      message: error instanceof Error ? error.message : String(error),
    });
  }
});

app.listen(port, () => {
  console.log(`RPC server running on http://localhost:${port}`);
});

🚀 How does defuss-rpc work?

Inside this package, you'll find the following relevant folders and files:

/
├── src/client.ts     # Client-side RPC implementation
├── src/server.ts     # Server-side RPC implementation  
├── src/types.d.ts    # TypeScript type definitions
├── tsconfig.json
├── LICENSE
├── package.json

Architecture

  1. Server-side:

    • Classes are registered with createRpcServer()
    • The rpcRoute handler processes incoming RPC calls
    • Method calls are dynamically routed to the appropriate class instances
    • Results are serialized using defuss-dson
  2. Client-side:

    • getRpcClient() creates a type-safe proxy-based client
    • Method calls are intercepted and sent as HTTP requests
    • Responses are automatically deserialized back to JavaScript objects
    • Full TypeScript intellisense and type checking
  3. Type Safety:

    • Share your API class definitions between client and server
    • The client provides the same interface as your server classes
    • Compile-time type checking ensures method signatures match

🧞 Commands

All commands are run from the root of the project, from a terminal:

| Command | Action | | :------------ | :----------------------------------------------- | | npm build | Build a new version of the RPC package. | | npm test | Run the test suite. | | npm publish | Publish a new version of the defuss-rpc package. |