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

@cellsweb/rpc-core

v1.0.26

Published

Core runtime engine for CellsWeb RPC API — O(1) dispatch, TypeBox schemas, Busboy multipart parser, and interactive docs

Readme

@cellsweb/rpc-core

Production-grade, zero-overhead RPC runtime engine for Node.js. Features $O(1)$ function dispatching, TypeBox schema validation, Busboy streaming multipart uploads, options-based middlewares, and auto-generated interactive documentation.

[!TIP] Consume this backend RPC server on the frontend using @cellsweb/rpc-client — the official client SDK with React/React Native hooks & automatic multipart form-data conversion!


Table of Contents


Key Features

  • Zero-Overhead Dispatching: Raw node:http execution loop with $O(1)$ in-memory function Map dispatching.
  • Strict Content-Type Contract: Enforces accepted contentType (application/json or multipart/form-data) per function. Mismatches are rejected automatically.
  • Streaming File Uploads: busboy streams uploaded files directly to temporary disk storage. Temp files are automatically cleaned up in finally blocks after request execution.
  • TypeBox Integration: End-to-end schema validation and type inference for headers, function inputs, middleware options, and function outputs.
  • Options-Based Middlewares: Central middlewares run declaratively per function with isolated options. Middlewares never touch function payload body data or files.
  • Interactive Documentation: Auto-renders a dark-mode interactive API documentation UI at /docs.

Comparison & Architectural Guidance

Framework Comparison Matrix

| Feature | CellsWeb RPC | tRPC | gRPC | |---|---|---|---| | Primary Platform Target | Universal (Web, Next.js, React Native / Expo, Node) | Web Monorepos (Next.js / React) | High-Performance Microservices | | Transport Layer | Standard HTTP/1.1 & HTTP/2 (Single /rpc endpoint) | HTTP/1.1 (JSON) | HTTP/2 (Protobuf Streams) | | Route Dispatching | $O(1)$ Hash Map Lookup (namespace.functionName) | Nested Router Tree Matcher | Protobuf Service / Method Dispatch | | File Uploads | Native Built-in (File, Files, Auto JS Object $\rightarrow$ FormData for RN/Web) | Requires custom FormData handlers / superjson | Custom stream chunking | | FormData Coercion | Built-in Schema Primitives (FormNum, FormBool) | Manual Zod/TypeBox coercions | N/A (Protobuf binary) | | Interactive Docs UI | Built-in Zero-Config /docs Engine (Dark theme, 2-column split view) | Requires 3rd-party (trpc-openapi + Swagger) | Requires 3rd-party (gRPC UI / Postman) | | Mobile / Expo Support | First-class (Handles RN { uri, name, type } natively) | Complex / Needs community workarounds | Requires Envoy gRPC-Web proxy | | Tooling & Setup | Interactive CLI Scaffolder (create-cellsweb-rpc-api) | Manual setup / Community templates | protoc compiler + code generator |


When to Choose CellsWeb RPC

  • Cross-Platform Applications (Web + Mobile): You are building a full-stack application spanning Web (React, Next.js) and Mobile (React Native / Expo) and need zero-hassle native file uploads and React hooks.
  • High-Performance $O(1)$ Function Routing: You want ultra-fast, predictable request dispatching without generic TypeScript router tree compilation bottlenecks or regex path matching overhead.
  • Form-Heavy & Upload-Rich APIs: You deal frequently with file uploads and form inputs, leveraging FormNum, FormBool, File, and Files schema primitives that resolve FormData string coercions automatically.
  • Zero-Config Developer Documentation: You want instant, interactive /docs UI for your API without configuring third-party Swagger or OpenAPI tools.
  • Modular Directory Architecture: You prefer clean, isolated directory structures (src/functions/{namespace}/{functionName}/) with automatic function and middleware discovery.

When NOT to Choose CellsWeb RPC

  • Non-TypeScript / Polyglot Backend Ecosystems: If your backend microservices are written in Rust, Go, or Java and communicate inter-service, gRPC with Protocol Buffers is a better fit.
  • Client-Defined GraphQL Queries: If your client apps require dynamic field masking and client-constructed graph queries over a single unified schema graph, GraphQL is more suitable.
  • Purely Internal Next.js App Router Monorepos Without Mobile: If you are exclusively building a Web-only Next.js app with zero mobile apps or external clients and want tRPC's server-side procedure chaining, tRPC is a popular alternative.

Installation

npm install @cellsweb/rpc-core
# or
pnpm add @cellsweb/rpc-core
# or
yarn add @cellsweb/rpc-core

Server Initialization Styles

The CoreRPC engine provides 4 flexible setup patterns:

Style 1: Direct Constructor

Pass configuration directly to new CoreRPC():

import { CoreRPC } from "@cellsweb/rpc-core";

const app = new CoreRPC({
  functionsDir: "./src/functions",     // default: "./src/functions"
  middlewaresDir: "./src/middlewares", // default: "./src/middlewares"
  port: 4000,                          // default: process.env.API_PORT || 4000
  host: "0.0.0.0",                     // default: "0.0.0.0"
  rpcEndpoint: "/rpc",                 // default: "/rpc" (also handles /api)
  docsEndpoint: "/docs",               // default: "/docs"
  docs: true,                          // default: true
});

await app.start();

Style 2: Instance Setters

Chain instance setter methods:

import { CoreRPC } from "@cellsweb/rpc-core";

const app = new CoreRPC({ docs: true });

app.useFunctions("./src/functions")
   .useMiddlewares("./src/middlewares")
   .setRpcEndpoint("/v1/rpc")
   .setDocsEndpoint("/v1/docs")
   .setPort(4000);

await app.listen();

Style 3: Static Chainable Builder

Use CoreRPC.configure() for fluent configuration:

import { CoreRPC } from "@cellsweb/rpc-core";

await CoreRPC.configure()
  .setPort(4000)
  .useFunctions("./src/functions")
  .useMiddlewares("./src/middlewares")
  .useRpcEndpoint("/rpc")
  .useDocsEndpoint("/docs")
  .listen();

Style 4: Custom HTTP Server / Express Mount

Use app.createHandler() to generate a standard Node.js RequestListener (req, res) for custom HTTP servers, Express, or Fastify:

import http from "node:http";
import { CoreRPC } from "@cellsweb/rpc-core";

const app = new CoreRPC({
  functionsDir: "./src/functions",
  middlewaresDir: "./src/middlewares",
  docs: true,
});

// Returns (req: IncomingMessage, res: ServerResponse) => Promise<void>
const handler = await app.createHandler();

// Listen on custom HTTP server:
const server = http.createServer(handler);
server.listen(4000, () => console.log("Server running on port 4000"));

Function Structure & Contracts

RPC functions are placed inside functions/{namespace}/{functionName}/:

1. JSON RPC Function

functions/public/getUser/constants.ts

import { Type } from "@sinclair/typebox";

export const metadata = {
  description: "Get user details by ID",
  isPublic: true,
  tags: ["users"],
};

export const contentType = "application/json" as const;

export const inputSchema = Type.Object({
  userId: Type.String({ minLength: 1, description: "Target User ID" }),
});

export const outputSchema = Type.Object({
  id: Type.String(),
  name: Type.String(),
  email: Type.String(),
});

functions/public/getUser/run.ts

import type { Static } from "@sinclair/typebox";
import type { MiddlewareContext } from "@cellsweb/rpc-core";
import { inputSchema, outputSchema } from "./constants";

type Input = Static<typeof inputSchema>;
type Output = Static<typeof outputSchema>;

export const run = async (input: Input, context: MiddlewareContext): Promise<Output> => {
  return {
    id: input.userId,
    name: "John Doe",
    email: "[email protected]",
  };
};

2. Multipart File Upload Function

Use the File() or Files() TypeBox schema helpers for uploads:

functions/public/uploadAvatar/constants.ts

import { Type } from "@sinclair/typebox";
import { File } from "@cellsweb/rpc-core";

export const metadata = {
  description: "Upload profile avatar image",
  isPublic: true,
  tags: ["uploads"],
};

export const contentType = "multipart/form-data" as const;

export const inputSchema = Type.Object({
  title: Type.String({ minLength: 1 }),
  avatar: File({
    maxSizeBytes: 5_000_000, // 5MB limit
    mimeTypes: ["image/png", "image/jpeg", "image/webp"],
    description: "Avatar image attachment",
  }),
});

export const outputSchema = Type.Object({
  url: Type.String(),
  size: Type.Number(),
});

functions/public/uploadAvatar/run.ts

import type { Static } from "@sinclair/typebox";
import type { MiddlewareContext } from "@cellsweb/rpc-core";
import { inputSchema, outputSchema } from "./constants";

type Input = Static<typeof inputSchema>;
type Output = Static<typeof outputSchema>;

export const run = async (input: Input, _context: MiddlewareContext): Promise<Output> => {
  // Access file properties:
  console.log(input.avatar.filename, input.avatar.byteLength, input.avatar.mimeType);

  // Read file as Buffer:
  const buffer = await input.avatar.toBuffer();

  return {
    url: `/uploads/${input.avatar.filename}`,
    size: input.avatar.byteLength,
  };
};

Custom Schema Types

@cellsweb/rpc-core exports four TypeBox-compatible schema helpers that extend the standard Type.* primitives for common RPC patterns.


File(options?) — Single File Upload

Declares a single uploaded file field in a multipart/form-data function schema.

import { File } from "@cellsweb/rpc-core";

avatar: File({
  maxSizeBytes: 5_000_000,                          // optional max file size in bytes
  mimeTypes: ["image/png", "image/jpeg"],           // optional MIME type allowlist
  description: "Profile avatar",                    // optional description shown in /docs
})

In run.ts, input.avatar is an UploadedFile instance:

input.avatar.filename     // original filename
input.avatar.mimeType     // detected MIME type
input.avatar.byteLength   // file size in bytes
await input.avatar.toBuffer()  // read as Buffer

Files(options?) — Multiple File Upload

Declares an array of uploaded files. Accepts the same options as File() plus maxCount:

import { Files } from "@cellsweb/rpc-core";

images: Files({
  maxCount: 10,                                     // optional max number of files
  maxSizeBytes: 2_000_000,
  mimeTypes: ["image/*"],
})

In run.ts, input.images is an UploadedFile[] array.


FormNum(options?) — Number via FormData

FormData serialises all non-file values as strings. FormNum accepts both "22" (string from FormData) and 22 (native number from JSON), and always delivers a real JavaScript number to run.ts — no manual casting needed.

import { FormNum } from "@cellsweb/rpc-core";

price:    FormNum(),   // "99.5" → 99.5  |  99.5 → 99.5
quantity: FormNum(),   // "-3"   → -3    |  -3   → -3

Accepted inputs: "22", "-3.5", 22, -3.5. Rejected inputs: "hello", "", true.

In run.ts, TypeScript type is number — no casting needed:

data.price * 1.2   // ✅ works directly

[!NOTE] Use FormNum instead of Type.Number() for multipart functions where the client sends numeric values via FormData.


FormBool(options?) — Boolean via FormData

Accepts "true"/"false" (string from FormData) and true/false (native boolean from JSON), always delivering a real boolean to run.ts.

import { FormBool } from "@cellsweb/rpc-core";

isPublic:  FormBool(),   // "true"  → true  |  true  → true
isDeleted: FormBool(),   // "false" → false |  false → false

In run.ts, TypeScript type is boolean:

if (data.isPublic) { ... }   // ✅ works directly

[!NOTE] Use FormBool instead of Type.Boolean() for multipart functions where the client sends boolean values via FormData.


Combined multipart example

import { Type } from "@sinclair/typebox";
import { File, Files, FormNum, FormBool } from "@cellsweb/rpc-core";

export const inputSchema = Type.Object({
  title:     Type.String({ minLength: 1 }),
  price:     FormNum(),
  inStock:   FormBool(),
  thumbnail: File({ maxSizeBytes: 5_000_000, mimeTypes: ["image/*"] }),
  gallery:   Files({ maxCount: 5, mimeTypes: ["image/*"] }),
});
// run.ts — all types are exactly what you'd expect
data.title       // string
data.price       // number  ✅ (even if sent as "99.5" via FormData)
data.inStock     // boolean ✅ (even if sent as "true" via FormData)
data.thumbnail   // UploadedFile
data.gallery     // UploadedFile[]

Central middlewares reside in src/middlewares/{middlewareName}/:

src/middlewares/tenantGuard/run.ts

import type { MiddlewareFn } from "@cellsweb/rpc-core";
import type { Static } from "@sinclair/typebox";
import { optionsSchema } from "./constants";

type Options = Static<typeof optionsSchema>;

export const run: MiddlewareFn<Options, { tenantId: string }> = async (
  { headers, options, function: fn },
  context,
) => {
  const tenantId = (headers["x-tenant-id"] as string) || undefined;
  if (!tenantId && !options.allowGlobal) {
    throw new Error("Missing x-tenant-id header");
  }

  // Augment and return context:
  return {
    ...context,
    tenantId: tenantId || "global",
  };
};

Functions declare middlewares in functions/{ns}/{fn}/middlewares.ts:

import { defineMiddlewares } from "@cellsweb/rpc-core";

export const middlewares = defineMiddlewares([
  { name: "tenantGuard", options: { allowGlobal: false } },
  { name: "rateLimit", options: { windowSeconds: 60, maxRequests: 100 } },
]);

Request & Response Payload Contract

Request (POST /rpc or POST /api)

{
  "namespace": "public",
  "functionName": "getUser",
  "data": {
    "userId": "usr_123"
  }
}

Success Response (HTTP 200)

{
  "status": "ok",
  "data": {
    "id": "usr_123",
    "name": "John Doe",
    "email": "[email protected]"
  },
  "meta": {
    "requestId": "clx123abc456",
    "durationMs": 1.42
  }
}

Error Response (HTTP 200)

{
  "status": "error",
  "code": "validation_error",
  "message": "userId: Expected string length greater or equal to 1",
  "meta": {
    "requestId": "clx123abc456",
    "durationMs": 0.85
  }
}

Interactive Docs UI & Health Check

  • Interactive Docs UI: Visit http://localhost:4000/docs in your browser.
  • Health Check: GET http://localhost:4000/health returns { "status": "ok", "timestamp": "..." }.

Scaffolding Projects (create-cellsweb-rpc-api)

To scaffold a complete project pre-configured with @cellsweb/rpc-core and interactive CLI generators:

# Using npm
npm create cellsweb-rpc-api@latest my-app

# Using npx
npx create-cellsweb-rpc-api@latest my-app

# Using pnpm
pnpm create cellsweb-rpc-api@latest my-app

License

MIT © Minhazur Rahman