@danscan/zod-jsonrpc
TypeScript icon, indicating that this package has built-in type declarations

2.1.0 • Public • Published

@danscan/zod-jsonrpc

Create JSON-RPC 2.0-compliant clients and servers that use Zod to validate requests and responses.

Just define your methods with Zod schemas and handlers, and zod-jsonrpc will handle the rest.

Features

  • JSON-RPC 2.0-compliant
  • Transport-agnostic (supports HTTP, WebSockets, and more)
  • Automatically handles batch requests, notification requests, and errors
  • Rich documentation comments referencing the JSON-RPC 2.0 specification
  • Exports Zod schemas and types for JSON-RPC requests, responses, errors and notifications
  • Automatic type safety for requests and responses
  • No dependencies; just one peer dependency: zod

Installation

bun add @danscan/zod-jsonrpc
yarn add @danscan/zod-jsonrpc
npm add @danscan/zod-jsonrpc

Usage

Creating a Server

import { createServer, method, JSONRPC2Error } from '@danscan/zod-jsonrpc';
import { z } from 'zod';

const server = createServer({
  greet: method({
    paramsSchema: z.object({ name: z.string() }),
    resultSchema: z.string(),
  }, ({ name }) => `Hello, ${name}!`),

  mustBeOdd: method({
    paramsSchema: z.object({ number: z.number() }),
    resultSchema: z.boolean(),
  }, ({ number }) => {
    const isOdd = number % 2 === 1;
    // Just throw a JSONRPC2Error to return an error response
    if (!isOdd) {
      throw new JSONRPC2Error.InvalidParams({
        message: 'Number must be odd',
        data: { number },
      });
    }
    return true;
  }),
});

// Handles single requests
const result = await server.request({
  id: 1,
  method: 'greet',
  params: { name: 'danscan' },
  jsonrpc: '2.0',
});
/*
{
  id: 1,
  result: 'Hello, danscan!',
  jsonrpc: '2.0',
}
*/

// Handles batch requests
const result = await server.request([
  { id: 1, method: 'greet', params: { name: 'danscan' }, jsonrpc: '2.0' },
  { id: 2, method: 'greet', params: { name: 'user' }, jsonrpc: '2.0' },
  { id: 3, method: 'mustBeOdd', params: { number: 4 }, jsonrpc: '2.0' },
]);
/*
[
  { id: 1, result: 'Hello, danscan!', jsonrpc: '2.0' },
  { id: 2, result: 'Hello, user!', jsonrpc: '2.0' },
  // Throwing a JSON-RPC 2.0 error returns a correct JSON-RPC 2.0 error response
  { id: 3, error: { code: -32602, message: 'Invalid params: Number must be odd', data: { number: 4 } }, jsonrpc: '2.0' },
]
*/

Usage with an HTTP Server

const jsonRpcServer = createServer({ /* methods */ });

// Simple Bun HTTP server
Bun.serve({
  fetch: async (req) => {
    const jsonRpcRequest = await req.json();
    const jsonRpcResponse = await jsonRpcServer.request(jsonRpcRequest);
    return Response.json(jsonRpcResponse);
  }
});

Creating a Client

import { createClient } from '@danscan/zod-jsonrpc';
import { z } from 'zod';

// Define a function that sends a JSON-RPC request to the server
const sendRequest = async (request: JSONRPCRequest) => {
  const response = await fetch('/jsonrpc', {
    method: 'POST',
    body: JSON.stringify(request),
  });
  return response.json();
};

// Create the client with the methods you want to call, and pass in the request function
const client = createClient({
  greet: method({
    paramsSchema: z.object({ name: z.string() }),
    resultSchema: z.string(),
  }),
}, sendRequest);

// Send a single request
const result = await client.greet({ name: 'danscan' });
// 'Hello, danscan!'

// Send a batch request, setting a convenient key for each request so you can easily match them up in the response
const result = await client.batch((ctx) => ({
  dan: ctx.greet({ name: 'Dan' }),
  drea: ctx.greet({ name: 'Drea' }),
}));
/*
{
  dan: { ok: true, value: 'Hello, Dan!' },
  drea: { ok: true, value: 'Hello, Drea!' },
}
*/

If you already have a server defined, you can create a client from it using the server.createClient method.

const client = server.createClient(async (request) => {
  // TODO: send the request to the server and return the result
});

Package Sidebar

Install

npm i @danscan/zod-jsonrpc

Weekly Downloads

73

Version

2.1.0

License

none

Unpacked Size

142 kB

Total Files

99

Last publish

Collaborators

  • danscan