@emkodev/json-rpc
v1.0.2
Published
JSON-RPC 2.0 specification compliant TypeScript implementation with full type safety
Maintainers
Readme
JSON-RPC 2.0 TypeScript Implementation
A specification-compliant JSON-RPC 2.0 implementation for TypeScript/Deno with full type safety and zero dependencies.
Features
- ✅ JSON-RPC 2.0 Spec Compliant - Follows the specification
- 🔒 Type Safe - Full TypeScript support with strict typing
- 🚀 Zero Dependencies - Pure TypeScript implementation
- 🧪 Thoroughly Tested - Comprehensive test suite
- 📦 Multiple Transports - HTTP, WebSocket, and custom transport support
- ⚡ High Performance - Efficient message handling and validation
- 🔄 Batch Support - Full batch request/response handling
- 📢 Notifications - Complete notification support
- 🛡️ Error Handling - Proper JSON-RPC error codes and handling
- ⚙️ Configurable - Flexible server and client options
Why Choose This Implementation?
- Zero Dependencies - No external packages to manage or audit
- TypeScript Native - Written from the ground up in TypeScript
- Multiple Transports - Built-in HTTP and WebSocket support
- Spec Compliant - Follows JSON-RPC 2.0 specification
- Batch Support - Full batch request and response handling
Installation
# Deno
deno add @emkodev/json-rpc
# Node.js
npx jsr add @emkodev/json-rpcQuick Start
Server
import { JsonRpcServer } from "@emkodev/json-rpc";
const server = new JsonRpcServer();
// Register methods
server.method("add", (params: [number, number]) => {
return params[0] + params[1];
});
server.method("greet", (params: { name: string }) => {
return `Hello, ${params.name}!`;
});
// Register notifications
server.notification("log", (params: { message: string }) => {
console.log(params.message);
});
// Handle requests
const request = JSON.stringify({
jsonrpc: "2.0",
method: "add",
params: [2, 3],
id: 1,
});
const response = await server.handle(request);
console.log(response); // {"jsonrpc":"2.0","result":5,"id":1}Client
import { HttpTransport, JsonRpcClient } from "@emkodev/json-rpc";
// HTTP transport
const transport = new HttpTransport("http://localhost:8080/rpc");
const client = new JsonRpcClient(transport);
// Method calls
const result = await client.call<number>("add", [2, 3]);
console.log(result); // 5
// Notifications
await client.notify("log", { message: "Hello World" });
// Batch requests
const responses = await client.batch([
{ type: "call", method: "add", params: [1, 2], id: 1 },
{ type: "call", method: "multiply", params: [3, 4], id: 2 },
{ type: "notify", method: "log", params: ["batch test"] },
]);API Reference
JsonRpcServer
const server = new JsonRpcServer({
allowNotifications: true, // Enable notifications (default: true)
allowBatch: true, // Enable batch requests (default: true)
maxBatchSize: 100, // Maximum batch size (default: 100)
timeout: 30000 // Request timeout in ms (default: 30000)
});
// Register method handler
server.method<TParams, TResult>(name: string, handler: JsonRpcMethodHandler<TParams, TResult>);
// Register notification handler
server.notification<TParams>(name: string, handler: JsonRpcNotificationHandler<TParams>);
// Handle JSON-RPC message
await server.handle(jsonString: string, context?: JsonRpcContext): Promise<string | null>;
// Process parsed message
await server.process(message: unknown, context?: JsonRpcContext): Promise<JsonRpcResponse | JsonRpcBatchResponse | null>;JsonRpcClient
const client = new JsonRpcClient(transport, {
timeout: 30000, // Request timeout in ms
retries: 3, // Number of retries
retryDelay: 1000, // Delay between retries
headers: {} // Custom headers
});
// Call method
await client.call<TResult, TParams>(method: string, params?: TParams, id?: JsonRpcId): Promise<TResult>;
// Send notification
await client.notify<TParams>(method: string, params?: TParams): Promise<void>;
// Batch requests
await client.batch(requests: BatchRequestItem[]): Promise<JsonRpcResponse[]>;
// Close connection
await client.close(): Promise<void>;Transports
HTTP Transport
import { HttpTransport } from "@emkodev/json-rpc";
const transport = new HttpTransport("http://localhost:8080/rpc", {
"Authorization": "Bearer token",
"Content-Type": "application/json",
});WebSocket Transport
import { WebSocketTransport } from "@emkodev/json-rpc";
const transport = new WebSocketTransport("ws://localhost:8080/rpc");Custom Transport
import { JsonRpcTransport } from "@emkodev/json-rpc";
class CustomTransport implements JsonRpcTransport {
async send(message: string): Promise<string> {
// Your transport implementation
return response;
}
async close?(): Promise<void> {
// Optional cleanup
}
}Error Handling
The library provides proper JSON-RPC 2.0 error handling:
import { JsonRpcErrorCode, JsonRpcServerError } from "@emkodev/json-rpc";
server.method("divide", (params: [number, number]) => {
if (params[1] === 0) {
throw JsonRpcServerError.invalidParams("Division by zero");
}
return params[0] / params[1];
});
// Client error handling
try {
await client.call("divide", [10, 0]);
} catch (error) {
if (error instanceof JsonRpcClientError) {
console.log(error.error.code); // -32602
console.log(error.error.message); // "Invalid params"
}
}Standard Error Codes
-32700Parse error-32600Invalid Request-32601Method not found-32602Invalid params-32603Internal error-32000to-32099Server error range
Batch Processing
// Server automatically handles batch requests
const batchRequest = [
{ jsonrpc: "2.0", method: "add", params: [1, 2], id: 1 },
{ jsonrpc: "2.0", method: "subtract", params: [5, 3], id: 2 },
{ jsonrpc: "2.0", method: "notify", params: ["test"] }, // notification
];
const responses = await server.handle(JSON.stringify(batchRequest));
// Returns array of responses (no response for notifications)
// Client batch requests
const responses = await client.batch([
{ type: "call", method: "add", params: [1, 2], id: 1 },
{ type: "call", method: "subtract", params: [5, 3], id: 2 },
{ type: "notify", method: "log", params: ["test"] },
]);Type Safety
Full TypeScript support with generic type parameters:
interface User {
id: number;
name: string;
email: string;
}
// Typed method handler
server.method<{ id: number }, User>("getUser", async (params) => {
// params is typed as { id: number }
return await findUser(params.id); // return type is User
});
// Typed client call
const user = await client.call<User, { id: number }>("getUser", { id: 123 });
// user is typed as UserTesting
Run the test suite:
# Run all tests
deno task test
# Run specific test suites
deno task test:types
deno task test:server
deno task test:client
deno task test:integration
# Watch mode
deno task test:watch
# Coverage report
deno task test:coverage
deno task coverageDevelopment
# Format code
deno task fmt
# Lint code
deno task lint
# Type check
deno task check
Specification Compliance
This implementation follows the JSON-RPC 2.0 Specification:
- ✅ Request/Response/Notification formats
- ✅ Batch processing
- ✅ Error handling with standard codes
- ✅ Reserved method names ("rpc." prefix)
- ✅ ID validation (string, number, null)
- ✅ Parameter passing (positional arrays or named objects)
- ✅ Version validation ("2.0" exactly)
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Project Status
Version 1.0.0 - Stable Release
✅ Spec Compliant - Follows JSON-RPC 2.0 specification requirements ✅ TypeScript Native - Written from the ground up in TypeScript ✅ Zero Dependencies - No external dependencies to manage ✅ Comprehensive Tests - Extensive test suite with passing tests ✅ Type Safe - Full TypeScript support with strict mode
Changelog
v1.0.0
- Stable release
- JSON-RPC 2.0 specification compliance
- Full TypeScript support with strict mode
- HTTP and WebSocket transports
- Comprehensive test suite
- Zero dependencies
