Skip to content

feat(handler): new defineJsonRpcHandler and defineJsonRpcWebSocketHandler#1180

Merged
pi0 merged 48 commits into
mainfrom
feat/json-rpc-handler
Feb 27, 2026
Merged

feat(handler): new defineJsonRpcHandler and defineJsonRpcWebSocketHandler#1180
pi0 merged 48 commits into
mainfrom
feat/json-rpc-handler

Conversation

@sandros94

@sandros94 sandros94 commented Aug 2, 2025

Copy link
Copy Markdown
Member

An experimental handler to support the JSON RPC spec.

Usage

you define it like a normal handler, but with the added params as its first callback argument:

const eventHandler = defineJsonRpcHandler({
  echo: (params, event) => {
    return `Received \`${params}\` on path \`${event.url.pathname}\``;
  },
});
app.post("/rpc"; eventHandler); // Must be a POST method

then you call it with a valid body, for example:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "echo",
  "params": ["Hello World"],
}

and you'll receive a json-rpc compatible response:

{
  "jsonrpc": "2.0",
  "id": 1, // Same id from the request
  "result": "Received `Hello World` on path `/rpc`",
}

it also support client-to-server notifications by simply omitting the id key, this will result in the server responding a 202

Personal considerations and future expansion

  • Support built-in schema validation (probably allowing for an object instead of directly the event callback)

Summary by CodeRabbit

  • New Features

    • Full JSON‑RPC 2.0 support: types, HTTP & WebSocket handlers, single & batch requests, notifications, validation, and HTTP↔RPC error mapping; public API surface expanded to expose JSON‑RPC types and handlers.
  • Tests

    • Comprehensive test suites covering success cases, batches, notifications, id handling, prototype/name edge cases, parse/validation errors, and HTTP error mappings.
  • Style

    • Minor spacing/formatting cleanup in an event handler chain.

@sandros94
sandros94 requested a review from pi0 as a code owner August 2, 2025 11:00
@sandros94 sandros94 self-assigned this Aug 2, 2025
@sandros94 sandros94 added the enhancement New feature or request label Aug 2, 2025
@codecov

codecov Bot commented Aug 2, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.26230% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/utils/json-rpc.ts 94.21% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread src/utils/json-rpc.ts Outdated
Comment thread src/utils/json-rpc.ts Outdated
Comment thread src/utils/json-rpc.ts Outdated
Comment thread src/utils/json-rpc.ts
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 2, 2025

Copy link
Copy Markdown

Deploying h3dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4d0d8c0
Status: ✅  Deploy successful!
Preview URL: https://d4d2748b.h3dev.pages.dev
Branch Preview URL: https://feat-json-rpc-handler.h3dev.pages.dev

View logs

@sandros94 sandros94 changed the title feat(handler): new jsonRpcHandler feat(handler): new defineJsonRpcHandler Aug 2, 2025
@pi0

pi0 commented Aug 27, 2025

Copy link
Copy Markdown
Member

Hi dear @sandros94 This PR is under my radar (and important!) but have to delay for more testing. In meantime you don't need to keep it updated ❤️

@sandros94

Copy link
Copy Markdown
Member Author

Hi dear @sandros94 This PR is under my radar (and important!) but have to delay for more testing. In meantime you don't need to keep it updated ❤️

Absolutely no problem, I also imagine that we all took some time off for the summer and should focus on more important things first, this is a long term one. This feature is also easy to implement in downstream projects, so no rush.

The reason why I kept updating it for a bit was because while testing it with some Json RPC clients (mainly MCP-related ones) I've found a couple of issues and DX improvements, but now it is complete for what I know atm

Comment thread src/utils/json-rpc.ts Outdated
@pi0 pi0 mentioned this pull request Feb 6, 2026
4 tasks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/json-rpc.test.ts`:
- Line 242: Update the test title string in the it(...) call that currently
reads "should handler primitive thrown errors and map to JSON-RPC error" to the
correct wording "should handle primitive thrown errors and map to JSON-RPC
error" (the test case in test/json-rpc.test.ts containing the it(...)
declaration).
🧹 Nitpick comments (1)
test/json-rpc.test.ts (1)

468-658: LGTM — HTTP error code mapping tests are comprehensive.

All major HTTP-to-JSON-RPC mappings are verified. Consider adding tests for 408 (timeout) and 422 (unprocessable entity) if those paths are expected to be exercised by consumers.

Comment thread test/json-rpc.test.ts Outdated
@sandros94

Copy link
Copy Markdown
Member Author

Development note: with dc503c0 I've dropped support for ReadableStream, something I've missinterpreted in my original implementation last august.

The JSON-RPC 2.0 spec expect a single, full, JSON object. It is the MCP spec that allows to have a GET endpoint on the same path which optionally supports ReadableStreams (eg: for server-to-client notifications), meaning it is out of scope from current PR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/json-rpc.test.ts`:
- Around line 233-253: The test currently expects non-HTTP exceptions (thrown by
the "error" handler) to surface error_.message as the JSON-RPC error.data, which
may leak internal details; update the JSON-RPC error mapping logic (the code
that builds the response in the eventHandler/error-to-JSON-RPC converter) so it
only includes error_.message in the "data" field when the error is an HTTPError
(or another explicitly safe error type) or when a configurable
“exposeErrorMessages” flag is enabled; otherwise omit the data field (or replace
with a generic value). Ensure the change references the existing mapping code
that checks error_ (the same place tested by the "should handle handler errors
and map to JSON-RPC error" test) and either adjust the test expectations or add
documentation/config to allow opt-in exposure.
🧹 Nitpick comments (1)
src/utils/json-rpc.ts (1)

54-57: JsonRpcMethod receives the full JsonRpcRequest but the docstring says "parameters".

The type signature passes data: JsonRpcRequest<I> (the full request object including jsonrpc, method, id, and params), but the JSDoc on Line 52 says "It receives the parameters from the request." This is accurate for how handlers destructure { params } in practice, but the first argument name data is the entire request object, not just params.

Consider renaming the parameter from data to request (or req) for clarity, since that's what it actually is.

Comment thread test/json-rpc.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/json-rpc-ws.test.ts`:
- Around line 180-189: The test name is misleading: it asserts a PARSE_ERROR but
is titled "...Invalid Request"; either change the implementation in
processJsonRpcBody to return INVALID_REQUEST for parsed non-object values
or—simpler—rename the test case to reflect the asserted behavior (e.g., change
the it description to "should return Parse error for non-object body") so the
test name matches the expected PARSE_ERROR (-32700) result; update only the test
description string in json-rpc-ws.test.ts and leave the assertion (error code
and message) as-is if you choose the rename approach.
🧹 Nitpick comments (2)
src/utils/json-rpc.ts (1)

389-395: method is typed as unknown — may cause TypeScript errors on strict configs.

req.method resolves to unknown (from Record<string, unknown>), but it's used as a string index into methodMap (line 395) and assigned to JsonRpcRequest.method (line 404). By this point, line 363 has already validated typeof req.method === "string", so the value is safe at runtime, but TypeScript doesn't narrow across the conditional into a separate variable assignment.

Consider adding a cast: const method = req.method as string;

Proposed fix
-  const method = req.method;
+  const method = req.method as string;
test/json-rpc-ws.test.ts (1)

329-350: Inner sendMessage shadows the outer helper — acceptable but worth noting.

The sendMessage in the "batch requests" describe block shadows the one in "message processing". This is fine since they have different method registrations, but if more describe blocks are added, consider extracting a shared configurable helper to reduce duplication.

Comment thread test/json-rpc-ws.test.ts Outdated
@sandros94 sandros94 changed the title feat(handler): new defineJsonRpcHandler feat(handler): new defineJsonRpcHandler and defineJsonRpcWebSocketHandler Feb 19, 2026
pi0 and others added 10 commits February 25, 2026 19:55
`createJsonRpcError` was dropping valid falsy `data` values (0, "", false).
`isValidId` was accepting NaN, Infinity, and fractional numbers which serialize
to null in JSON, silently corrupting the response id.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

@pi0 pi0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@pi0
pi0 merged commit 0575cb8 into main Feb 27, 2026
7 checks passed
@pi0
pi0 deleted the feat/json-rpc-handler branch February 27, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants