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

@srpc.org/core

v0.20.5

Published

A lightweight, type-safe RPC framework for TypeScript

Readme

@srpc.org/core

A lightweight, type-safe RPC framework for TypeScript with automatic type inference, multiple runtime support, and zero dependencies.

Installation

# Using JSR (recommended)
deno add @srpc.org/core
npx jsr add @srpc.org/core
yarn dlx jsr add @srpc.org/core
pnpm dlx jsr add @srpc.org/core
bunx jsr add @srpc.org/core

Features

  • Type-safe: Full TypeScript support with automatic type inference
  • Zero dependencies: Lightweight core with no external dependencies
  • Runtime agnostic: Works with Node.js, Deno, Bun, Cloudflare Workers, and more
  • Multiple adapters: Support for both Node.js HTTP and standard Fetch API
  • Nested routers: Organize procedures into nested namespaces
  • Custom serialization: Pluggable serialization layer
  • Error handling: Built-in error types with HTTP status code mapping

Quick Start

Server Setup

Using Fetch API (Recommended for Edge Runtimes)

import { initSRPC, srpcFetchApi } from "@srpc.org/core/server";

// Initialize SRPC
const s = initSRPC();

// Define your router
const appRouter = s.router({
  sayHello: async (_, name: string) => {
    return `Hello ${name}!`;
  },
  getUser: async (_, id: number) => {
    return { id, name: "John Doe", email: "[email protected]" };
  },
});

// Export the router type for client usage
export type AppRouter = typeof appRouter;

// Create Fetch API handler
const { fetch: handleRequest } = srpcFetchApi({
  router: appRouter,
  endpoint: "/api",
});

// Use with any framework supporting Fetch API
export default {
  fetch: handleRequest,
};

Using Node.js HTTP Server

import { initSRPC } from "@srpc.org/core/server";
import { createSrpcServer } from "@srpc.org/core/server";

const s = initSRPC();

const appRouter = s.router({
  sayHello: async (_, name: string) => {
    return `Hello ${name}!`;
  },
});

export type AppRouter = typeof appRouter;

// Create Node.js server
const server = createSrpcServer({
  router: appRouter,
  endpoint: "/api",
});

server.listen(3000, () => {
  console.log("SRPC server listening on port 3000");
});

Client Setup

import { createSRPCClient } from "@srpc.org/core/client";
import type { AppRouter } from "./server";

// Create type-safe client
const client = createSRPCClient<AppRouter>({
  endpoint: "http://localhost:3000/api",
});

// Call procedures with full type safety
const greeting = await client.sayHello("World"); // Type: string
const user = await client.getUser(1); // Type: { id: number, name: string, email: string }

Advanced Usage

Nested Routers

Organize your procedures into logical groups:

import { initSRPC } from "@srpc.org/core/server";

const s = initSRPC();

// Admin procedures
const adminRouter = s.router({
  createUser: async (_, data: { name: string; email: string }) => {
    // Create user logic
    return { id: 1, ...data };
  },
  deleteUser: async (_, id: number) => {
    // Delete user logic
    return { success: true };
  },
});

// Public user procedures
const usersRouter = s.router({
  getUser: async (_, id: number) => {
    return { id, name: "John Doe" };
  },
  admin: adminRouter, // Nested router
});

// Main app router
const appRouter = s.router({
  users: usersRouter,
  sayHello: async (_, name: string) => `Hello ${name}!`,
});

// Client usage:
// client.users.getUser(1)
// client.users.admin.createUser({ name: "Jane", email: "[email protected]" })
// client.sayHello("World")

Context

Add context (authentication, database connections, etc.) to your procedures:

import { initSRPC, srpcFetchApi } from "@srpc.org/core/server";

// Define your context type
type Context = {
  user?: { id: number; name: string };
  db: Database;
};

// Initialize with context type
const s = initSRPC().context<Context>();

// Use context in procedures
const appRouter = s.router({
  getCurrentUser: async (ctx) => {
    if (!ctx.user) throw new Error("Not authenticated");
    return ctx.user;
  },
  getPost: async (ctx, id: number) => {
    return await ctx.db.posts.findById(id);
  },
});

// Provide context creation function
const { fetch: handleRequest } = srpcFetchApi({
  router: appRouter,
  endpoint: "/api",
  createContext: async (req) => {
    const user = await authenticateRequest(req);
    return { user, db: getDatabase() };
  },
});

Error Handling

Use SRPCError for proper HTTP status code mapping:

import { initSRPC } from "@srpc.org/core/server";
import { SRPCError } from "@srpc.org/core";

const s = initSRPC();

const appRouter = s.router({
  getUser: async (_, id: number) => {
    const user = await db.users.findById(id);
    if (!user) {
      throw new SRPCError("User not found", "NOT_FOUND"); // Returns HTTP 404
    }
    return user;
  },
  deleteUser: async (_, id: number) => {
    if (!hasPermission()) {
      throw new SRPCError("Unauthorized", "UNAUTHORIZED"); // Returns HTTP 401
    }
    await db.users.delete(id);
    return { success: true };
  },
});

// Client-side error handling
try {
  await client.getUser(999);
} catch (error) {
  if (error instanceof SRPCError) {
    console.log(error.code); // "NOT_FOUND"
    console.log(error.message); // "User not found"
  }
}

Custom Serialization

Use custom serializers for complex data types:

import { createSRPCClient } from "@srpc.org/core/client";
import type { Serializer } from "@srpc.org/core/shared";

// Example: Using superjson for Date, Map, Set support
import superjson from "superjson";

const customSerializer: Serializer = {
  serialize: (value) => superjson.stringify(value),
  deserialize: (value) => superjson.parse(value),
};

const client = createSRPCClient<AppRouter>({
  endpoint: "http://localhost:3000/api",
  transformer: customSerializer,
});

// Server-side
const { fetch: handleRequest } = srpcFetchApi({
  router: appRouter,
  endpoint: "/api",
  transformer: customSerializer,
});

Type Inference Utilities

Extract input and output types from your router:

import type { InferRouterInputs, InferRouterOutputs } from "@srpc.org/core";
import type { AppRouter } from "./server";

// Get all input types
type RouterInputs = InferRouterInputs<AppRouter>;
type GetUserInput = RouterInputs["getUser"]; // [id: number]

// Get all output types
type RouterOutputs = InferRouterOutputs<AppRouter>;
type GetUserOutput = RouterOutputs["getUser"]; // { id: number, name: string, email: string }

Server-Side Calling

Call procedures directly on the server without HTTP:

import { createSRPCCaller } from "@srpc.org/core/server";
import { appRouter } from "./router";

const caller = createSRPCCaller({
  router: appRouter,
  createContext: async () => ({ user: null, db: getDatabase() }),
});

// Call procedures directly
const result = await caller.sayHello("World");

API Reference

Server

  • initSRPC() - Initialize SRPC builder
  • srpcFetchApi(options) - Create Fetch API handler
  • createSrpcServer(options) - Create Node.js HTTP server
  • createSRPCCaller(options) - Create server-side caller

Client

  • createSRPCClient<TRouter>(options) - Create type-safe client

Shared

  • SRPCError - Error class with HTTP status mapping
  • ErrorCodes - Available error codes
  • Serializer - Serialization interface
  • InferRouterInputs<TRouter> - Extract input types
  • InferRouterOutputs<TRouter> - Extract output types

Error Codes

  • BAD_REQUEST → HTTP 400
  • UNAUTHORIZED → HTTP 401
  • FORBIDDEN → HTTP 403
  • NOT_FOUND → HTTP 404
  • UNSUPPORTED_MEDIA_TYPE → HTTP 415
  • INTERNAL_SERVER_ERROR → HTTP 500
  • NOT_IMPLEMENTED → HTTP 501
  • GENERIC_ERROR → HTTP 500

License

MIT