https://github.com/MetaMask/json-rpc-engine
A tool for processing JSON RPC
https://github.com/MetaMask/json-rpc-engine
Last synced: 6 months ago
JSON representation
A tool for processing JSON RPC
- Host: GitHub
- URL: https://github.com/MetaMask/json-rpc-engine
- Owner: MetaMask
- License: isc
- Archived: true
- Created: 2016-09-29T10:40:15.000Z (over 9 years ago)
- Default Branch: main
- Last Pushed: 2023-11-09T00:34:27.000Z (about 2 years ago)
- Last Synced: 2025-06-15T16:00:52.178Z (7 months ago)
- Language: TypeScript
- Homepage:
- Size: 3.04 MB
- Stars: 160
- Watchers: 75
- Forks: 63
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# `@metamask/json-rpc-engine`
⚠️ PLEASE READ ⚠️
This package has been migrated to our core monorepo, and this repository has been archived. Please note that all future development and feature releases will take place in the core repository.
A tool for processing JSON-RPC requests and responses.
## Usage
```js
const { JsonRpcEngine } = require('@metamask/json-rpc-engine');
const engine = new JsonRpcEngine();
```
Build a stack of JSON-RPC processors by pushing middleware to the engine.
```js
engine.push(function (req, res, next, end) {
res.result = 42;
end();
});
```
Requests are handled asynchronously, stepping down the stack until complete.
```js
const request = { id: 1, jsonrpc: '2.0', method: 'hello' };
engine.handle(request, function (err, response) {
// Do something with response.result, or handle response.error
});
// There is also a Promise signature
const response = await engine.handle(request);
```
Middleware have direct access to the request and response objects.
They can let processing continue down the stack with `next()`, or complete the request with `end()`.
```js
engine.push(function (req, res, next, end) {
if (req.skipCache) return next();
res.result = getResultFromCache(req);
end();
});
```
By passing a _return handler_ to the `next` function, you can get a peek at the result before it returns.
```js
engine.push(function (req, res, next, end) {
next(function (cb) {
insertIntoCache(res, cb);
});
});
```
If you specify a `notificationHandler` when constructing the engine, JSON-RPC notifications passed to `handle()` will be handed off directly to this function without touching the middleware stack:
```js
const engine = new JsonRpcEngine({ notificationHandler });
// A notification is defined as a JSON-RPC request without an `id` property.
const notification = { jsonrpc: '2.0', method: 'hello' };
const response = await engine.handle(notification);
console.log(typeof response); // 'undefined'
```
Engines can be nested by converting them to middleware using `JsonRpcEngine.asMiddleware()`:
```js
const engine = new JsonRpcEngine();
const subengine = new JsonRpcEngine();
engine.push(subengine.asMiddleware());
```
### `async` Middleware
If you require your middleware function to be `async`, use `createAsyncMiddleware`:
```js
const { createAsyncMiddleware } = require('@metamask/json-rpc-engine');
let engine = new RpcEngine();
engine.push(
createAsyncMiddleware(async (req, res, next) => {
res.result = 42;
next();
}),
);
```
`async` middleware do not take an `end` callback.
Instead, the request ends if the middleware returns without calling `next()`:
```js
engine.push(
createAsyncMiddleware(async (req, res, next) => {
res.result = 42;
/* The request will end when this returns */
}),
);
```
The `next` callback of `async` middleware also don't take return handlers.
Instead, you can `await next()`.
When the execution of the middleware resumes, you can work with the response again.
```js
engine.push(
createAsyncMiddleware(async (req, res, next) => {
res.result = 42;
await next();
/* Your return handler logic goes here */
addToMetrics(res);
}),
);
```
You can freely mix callback-based and `async` middleware:
```js
engine.push(function (req, res, next, end) {
if (!isCached(req)) {
return next((cb) => {
insertIntoCache(res, cb);
});
}
res.result = getResultFromCache(req);
end();
});
engine.push(
createAsyncMiddleware(async (req, res, next) => {
res.result = 42;
await next();
addToMetrics(res);
}),
);
```
### Teardown
If your middleware has teardown to perform, you can assign a method `destroy()` to your middleware function(s),
and calling `JsonRpcEngine.destroy()` will call this method on each middleware that has it.
A destroyed engine can no longer be used.
```js
const middleware = (req, res, next, end) => {
/* do something */
};
middleware.destroy = () => {
/* perform teardown */
};
const engine = new JsonRpcEngine();
engine.push(middleware);
/* perform work */
// This will call middleware.destroy() and destroy the engine itself.
engine.destroy();
// Calling any public method on the middleware other than `destroy()` itself
// will throw an error.
engine.handle(req);
```
### Gotchas
Handle errors via `end(err)`, _NOT_ `next(err)`.
```js
/* INCORRECT */
engine.push(function (req, res, next, end) {
next(new Error());
});
/* CORRECT */
engine.push(function (req, res, next, end) {
end(new Error());
});
```
However, `next()` will detect errors on the response object, and cause
`end(res.error)` to be called.
```js
engine.push(function (req, res, next, end) {
res.error = new Error();
next(); /* This will cause end(res.error) to be called. */
});
```
## Running tests
Build the project if not already built:
```bash
yarn build
```
```bash
yarn test
```