Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

json-rpc-engine

MetaMask2.4mISC6.1.0TypeScript support: included

A tool for processing JSON-RPC messages.

readme

json-rpc-engine

A tool for processing JSON-RPC requests and responses.

Usage

const { JsonRpcEngine } = require('json-rpc-engine')

let engine = new JsonRpcEngine()

Build a stack of JSON-RPC processors by pushing middleware to the engine.

engine.push(function(req, res, next, end){
  res.result = 42
  end()
})

Requests are handled asynchronously, stepping down the stack until complete.

let 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().

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.

engine.push(function(req, res, next, end){
  next(function(cb){
    insertIntoCache(res, cb)
  })
})

Engines can be nested by converting them to middleware using JsonRpcEngine.asMiddleware():

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:

const { createAsyncMiddleware } = require('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():

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.

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:

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)
}))

Gotchas

Handle errors via end(err), NOT next(err).

/* 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.

engine.push(function(req, res, next, end){
  res.error = new Error()
  next() /* This will cause end(res.error) to be called. */
})

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

6.1.0 - 2020-11-20

Added

  • Add PendingJsonRpcResponse interface for use in middleware (#75)

Changed

  • Use async/await and try/catch instead of Promise methods everywhere (#74)
    • Consumers may notice improved stack traces on certain platforms.

6.0.0 - 2020-11-19

Added

  • Add docstrings for public JsonRpcEngine methods (#70)

Changed

  • (BREAKING) Refactor exports (#69)
    • All exports are now named, and available via the package entry point.
    • All default exports have been removed.
  • (BREAKING) Convert asMiddleware to instance method (#69)
    • The asMiddleware export has been removed.
  • (BREAKING) Add runtime typechecks to JsonRpcEngine.handle(), and error responses if they fail (#70)
    • Requests will now error if:
      • The request is not a plain object, or if the method property is not a string. Empty strings are allowed.
      • A next middleware callback is called with a truthy, non-function parameter.
  • Migrate to TypeScript (#69)
  • Hopefully improve stack traces by removing uses of Promise.then and .catch internally (#70)
  • Make some internal JsonRpcEngine methods static (#71)

5.4.0 - 2020-11-07

Changed

  • Make the TypeScript types not terrible (#66, #67)

5.3.0 - 2020-07-30

Changed

  • Response object errors no longer include a stack property

5.2.0 - 2020-07-24

Added

  • Promise signatures for engine.handle (#55)
    • So, in addition to engine.handle(request, callback), you can do e.g. await engine.handle(request).

Changed

  • Remove async and promise-to-callback dependencies
    • These dependencies were used internally for middleware flow control. They have been replaced with Promises and native async/await, which means that some operations are no longer eagerly executed. This change may affect consumers that depend on the eager execution of middleware during request processing, outside of middleware functions and request handlers.
      • In general, it is a bad practice to work with state that depends on middleware execution, while the middleware are executing.