Crate ajj

Source
Expand description

ajj: A JSON-RPC router inspired by axum’s Router.

This crate provides a way to define a JSON-RPC router that can be used to route requests to handlers. It is inspired by the axum crate’s axum::Router.

§Basic usage

The Router type is the main type provided by this crate. It is used to register JSON-RPC methods and their handlers.

use ajj::{Router, HandlerCtx, ResponsePayload};

// Provide methods called "double" and "add" to the router.
let router = Router::<u64>::new()
  .route("add", |params: u64, state: u64| async move {
    Ok::<_, ()>(params + state)
  })
  .with_state(3u64)
  .route("double", |params: u64| async move {
    Ok::<_, ()>(params * 2)
  })
  // Routes get a ctx, which can be used to send notifications.
  .route("notify", |ctx: HandlerCtx| async move {
    if ctx.notifications().is_none() {
      // This error will appear in the ResponsePayload's `data` field.
      return Err("notifications are disabled");
    }

    let req_id = 15u8;

    tokio::task::spawn_blocking(move || {
      // something expensive goes here
      let result = 100_000_000;
      let _ = ctx.notify(&serde_json::json!({
        "req_id": req_id,
        "result": result,
      }));
    });
    Ok(req_id)
  })
  .route("error_example", || async {
    // This will appear in the ResponsePayload's `message` field.
    ResponsePayload::<(), ()>::internal_error_message("this is an error".into())
  });

§Handlers

Methods are routed via the Handler trait, which is blanket implemented for many async functions. Handler contain implement the logic executed when calling methods on the JSON-RPC router.

Handlers can return either

  • Result<T, E> where T: Serialize, E: Serialize
  • ResponsePayload<T, E> where T: Serialize, E: Serialize

These types will be serialized into the JSON-RPC response. The T type represents the result of the method, and the E type represents an error response. The E type is optional, and can be set to () if no error response is needed.

See the Handler trait docs for more information.

§Serving the Router

We recommend axum for serving the router over HTTP. When the "axum" feature flag is enabled, The Router provides Router::into_axum(path: &str) to instantiate a new axum::Router, and register the router to handle requests. You can then serve the axum::Router as normal, or add additional routes to it.

// Instantiate a new axum router, and register the JSON-RPC router to handle
// requests at the `/rpc` path, and serve it on port 3000.
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, router.into_axum("/rpc")).await.unwrap();

Routers can also be served over axum websockets. When both axum and pubsub features are enabled, the pubsub module provides pubsub::AxumWsCfg and the pubsub::ajj_websocket axum handler. This handler will serve the router over websockets at a specific route. The router is a property of the AxumWsCfg object, and is passed to the handler via axum’s State extractor.

// The config object contains the tokio runtime handle, and the
// notification buffer size.
let cfg = AxumWsCfg::new(router);

axum
    .route("/ws", axum::routing::any(ajj_websocket))
    .with_state(cfg)

For IPC and non-axum WebSocket connections, the pubsub module provides implementations of the Connect trait for std::net::SocketAddr to create simple WS servers, and interprocess::local_socket::ListenerOptions to create simple IPC servers. We generally recommend using axum for WebSocket connections, as it provides a more complete and robust implementation, however, users needing additional control, or wanting to avoid the axum dependency can use the pubsub module directly.

// Serve the router over websockets on port 3000.
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 3000));
// The shutdown object will stop the server when dropped.
let shutdown = addr.serve(router).await.unwrap();

See the pubsub module documentation for more information.

Re-exports§

pub use tower;
pub use serde_json;

Modules§

pubsubpubsub
Pubsub serving utils for Routers.

Structs§

BatchFuture
A collection of RouteFutures that are executed concurrently.
ErrorPayload
A JSON-RPC 2.0 error object.
HandlerArgs
Arguments passed to a handler.
HandlerCtx
A context for handler requests that allow the handler to send notifications and spawn long-running tasks (e.g. subscriptions).
MethodId
A unique internal identifier for a method.
Params
Hint type for differentiating certain handler impls. See the Handler trait “Handler argument type inference” section for more information.
RawValue
Re-export of the serde_json crate, primarily to provide the RawValue type. Reference to a range of bytes encompassing a single valid JSON value in the input data.
RouteFuture
A future produced by the Router.
Router
A JSON-RPC router. This is the top-level type for handling JSON-RPC requests. It is heavily inspired by the axum::Router type.
State
Hint type for differentiating certain handler impls. See the Handler trait “Handler argument type inference” section for more information.

Enums§

NotifyError
Errors that can occur when sending notifications.
RegistrationError
Errors that can occur when registering a method.
ResponsePayload
A JSON-RPC 2.0 response payload.

Traits§

BorrowedRpcObject
An object that can be both sent and received over RPC, borrowing from the the deserialization context.
Handler
A trait describing handlers for JSON-RPC methods.
RpcBorrow
An object that can be received over RPC, borrowing from the the deserialization context.
RpcObject
An object that can be both sent and received over RPC.
RpcRecv
An object that can be received over RPC.
RpcSend
An object that can be sent over RPC.