API Reference
This page lists the public API exposed by the Vix webrpc module.
WebRPC provides a small transport-agnostic RPC core. It defines request and response envelopes, structured errors, execution context, method routing, and dispatching for single calls, notifications, and batches.
The module is designed around explicit values:
RpcRequest
-> Context
-> RpcHandler
-> RpcResult
-> RpcResponseHandlers return success or failure as values. Normal RPC failures are represented by RpcError, not by exceptions.
Header
Use the public WebRPC aggregation header:
#include <vix/webrpc/webrpc.hpp>The module uses the Vix JSON token model:
#include <vix/json/Simple.hpp>For examples that print output:
#include <vix/print.hpp>The public aggregation header includes:
#include <vix/webrpc/Error.hpp>
#include <vix/webrpc/Request.hpp>
#include <vix/webrpc/Response.hpp>
#include <vix/webrpc/Context.hpp>
#include <vix/webrpc/Router.hpp>
#include <vix/webrpc/Dispatcher.hpp>Namespace
All WebRPC APIs live in the vix::webrpc namespace.
namespace vix::webrpc
{
}WebRPC works with JSON-like values from:
namespace vix::json
{
}RpcError
RpcError represents one structured RPC failure.
struct RpcError
{
std::string code{};
std::string message{};
vix::json::token details{nullptr};
RpcError() = default;
RpcError(
std::string code_,
std::string message_);
RpcError(
std::string code_,
std::string message_,
vix::json::token details_);
bool valid() const noexcept;
bool has_details() const noexcept;
vix::json::token to_json_token() const;
vix::json::token to_json() const;
static RpcErrorParseResult parse(const vix::json::token &root);
static RpcError method_not_found(std::string_view method);
static RpcError invalid_params(std::string_view reason);
static RpcError parse_error(std::string_view reason);
static RpcError internal_error(std::string_view msg);
};Fields
| Field | Purpose |
|---|---|
code | Stable machine-readable error code. |
message | Human-readable error message. |
details | Optional structured details as a JSON token. |
Standard helpers
| Helper | Error code | Purpose |
|---|---|---|
RpcError::method_not_found(...) | METHOD_NOT_FOUND | Method is not registered in the router. |
RpcError::invalid_params(...) | INVALID_PARAMS | Request parameters are not acceptable. |
RpcError::parse_error(...) | PARSE_ERROR | Payload or envelope is malformed. |
RpcError::internal_error(...) | INTERNAL_ERROR | Internal server-side failure. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
auto error =
vix::webrpc::RpcError::invalid_params("id must be an integer");
vix::print("code:", error.code);
vix::print("message:", error.message);
return 0;
}RpcError serialization
Use to_json() or to_json_token() to serialize an error.
vix::json::token to_json_token() const;
vix::json::token to_json() const;Serialized shape with details:
{
"code": "INVALID_PARAMS",
"message": "Invalid RPC parameters",
"details": {
"reason": "id must be an integer"
}
}Serialized shape without details:
{
"code": "INTERNAL_ERROR",
"message": "database unavailable"
}Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::webrpc;
RpcError error = RpcError::method_not_found("user.get");
auto token = error.to_json();
const auto *object = token.as_object_ptr().get();
if (!object)
{
return 1;
}
vix::print("code:", object->get_string_or("code", ""));
return 0;
}RpcErrorParseResult
RpcErrorParseResult is returned by RpcError::parse(...).
struct RpcErrorParseResult
{
static RpcErrorParseResult ok(RpcError v);
static RpcErrorParseResult fail(RpcError e);
bool ok() const noexcept;
const RpcError &value() const noexcept;
RpcError &value() noexcept;
const RpcError &error() const noexcept;
RpcError &error() noexcept;
};Use ok() before reading value().
auto parsed = vix::webrpc::RpcError::parse(raw);
if (parsed.ok())
{
const auto &error = parsed.value();
}
else
{
const auto &parse_error = parsed.error();
}RpcError::parse
Parses a JSON-like token into RpcError.
static RpcErrorParseResult
parse(const vix::json::token &root);Parsing rules:
| Rule | Failure |
|---|---|
| Root must be an object. | PARSE_ERROR |
code must exist. | PARSE_ERROR |
message must exist. | PARSE_ERROR |
code must be a string. | PARSE_ERROR |
message must be a string. | PARSE_ERROR |
code must not be empty. | PARSE_ERROR |
details, when present, can be any token. | No parse failure. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
token raw = obj({
"code", "INVALID_PARAMS",
"message", "Invalid RPC parameters",
"details", obj({
"reason", "id must be an integer",
}),
});
RpcErrorParseResult parsed = RpcError::parse(raw);
if (!parsed.ok())
{
vix::print("invalid error object");
return 1;
}
vix::print("code:", parsed.value().code);
return 0;
}RpcRequest
RpcRequest represents one RPC call.
struct RpcRequest
{
vix::json::token id{nullptr};
std::string method{};
vix::json::token params{nullptr};
RpcRequest() = default;
RpcRequest(
vix::json::token id_,
std::string method_,
vix::json::token params_);
bool has_id() const noexcept;
bool valid() const noexcept;
vix::json::token to_json() const;
static std::variant<RpcRequest, RpcError>
parse(const vix::json::token &root);
const vix::json::kvs *params_object_ptr() const noexcept;
const vix::json::array_t *params_array_ptr() const noexcept;
const vix::json::token *
param_ptr(std::string_view key) const noexcept;
};Request shape
{
"id": 1,
"method": "math.add",
"params": {
"a": 7,
"b": 5
}
}Fields
| Field | Purpose |
|---|---|
id | Optional request id. Null means notification. |
method | Required RPC method name. |
params | Optional JSON-like parameters. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
RpcRequest request{
token(1LL),
"ping",
token(nullptr),
};
vix::print("method:", request.method);
vix::print("has id:", request.has_id());
return 0;
}RpcRequest::parse
Parses a request envelope.
static std::variant<RpcRequest, RpcError>
parse(const vix::json::token &root);Parsing rules:
| Rule | Failure |
|---|---|
| Root must be an object. | PARSE_ERROR |
method must exist. | INVALID_PARAMS |
method must be a non-empty string. | INVALID_PARAMS |
id, when present, must be null, string, or integer. | INVALID_PARAMS |
params, when present, can be any token. | No envelope failure. |
Example:
#include <variant>
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
token raw = obj({
"id", 1LL,
"method", "ping",
});
auto parsed = RpcRequest::parse(raw);
if (std::holds_alternative<RpcError>(parsed))
{
const auto &error = std::get<RpcError>(parsed);
vix::print("request error:", error.code);
return 1;
}
const RpcRequest &request = std::get<RpcRequest>(parsed);
vix::print("method:", request.method);
return 0;
}RpcRequest helpers
has_id
bool has_id() const noexcept;Returns true when id is not null.
A request without an id is treated as a notification by the dispatcher.
valid
bool valid() const noexcept;Returns true when method is not empty.
to_json
vix::json::token to_json() const;Serializes the request to a JSON-like token.
params_object_ptr
const vix::json::kvs *
params_object_ptr() const noexcept;Returns params as an object pointer, or nullptr when params is not an object.
params_array_ptr
const vix::json::array_t *
params_array_ptr() const noexcept;Returns params as an array pointer, or nullptr when params is not an array.
param_ptr
const vix::json::token *
param_ptr(std::string_view key) const noexcept;Returns one object parameter by key, or nullptr.
RpcResponse
RpcResponse represents the output envelope for one RPC call.
struct RpcResponse
{
vix::json::token id{nullptr};
vix::json::token result{nullptr};
RpcError error{};
bool has_error{false};
RpcResponse() = default;
static RpcResponse ok(
vix::json::token id_,
vix::json::token result_);
static RpcResponse fail(
vix::json::token id_,
RpcError err);
bool is_notification() const noexcept;
bool ok() const noexcept;
vix::json::token to_json() const;
static std::variant<RpcResponse, RpcError>
parse(const vix::json::token &root);
};Success shape
{
"id": 1,
"result": {
"pong": true
}
}Error shape
{
"id": 1,
"error": {
"code": "METHOD_NOT_FOUND",
"message": "RPC method not found",
"details": {
"method": "missing.method"
}
}
}A response contains either result or error, not both.
RpcResponse::ok
Creates a success response.
static RpcResponse ok(
vix::json::token id_,
vix::json::token result_);Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
RpcResponse response = RpcResponse::ok(
token(1LL),
obj({
"pong", true,
}));
vix::print("ok:", response.ok());
return 0;
}RpcResponse::fail
Creates an error response.
static RpcResponse fail(
vix::json::token id_,
RpcError err);Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
RpcResponse response = RpcResponse::fail(
token(1LL),
RpcError::invalid_params("id must be an integer"));
if (!response.ok())
{
vix::print("error:", response.error.code);
}
return 0;
}RpcResponse::parse
Parses a response envelope.
static std::variant<RpcResponse, RpcError>
parse(const vix::json::token &root);Parsing rules:
| Rule | Failure |
|---|---|
| Root must be an object. | PARSE_ERROR |
id, when present, must be null, string, or integer. | INVALID_PARAMS |
Response must contain result or error. | INVALID_PARAMS |
Response cannot contain both result and error. | INVALID_PARAMS |
error must be a valid RpcError. | Parse failure from RpcError::parse(...). |
Example:
#include <variant>
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
token raw = obj({
"id", 1LL,
"result", obj({
"pong", true,
}),
});
auto parsed = RpcResponse::parse(raw);
if (std::holds_alternative<RpcError>(parsed))
{
const auto &error = std::get<RpcError>(parsed);
vix::print("response error:", error.code);
return 1;
}
const RpcResponse &response = std::get<RpcResponse>(parsed);
vix::print("ok:", response.ok());
return 0;
}RpcResponse helpers
is_notification
bool is_notification() const noexcept;Returns true when the response id is null.
ok
bool ok() const noexcept;Returns true when the response is a success response.
to_json
vix::json::token to_json() const;Serializes the response into a JSON-like token.
Context
Context is the execution context passed to every RPC handler.
struct Context
{
using MetaMap =
std::unordered_map<std::string, std::string>;
std::string_view method{};
const vix::json::token ¶ms;
const vix::json::token &id;
std::string_view transport{};
const MetaMap *meta{nullptr};
Context(
std::string_view method_,
const vix::json::token ¶ms_,
const vix::json::token &id_,
std::string_view transport_ = {},
const MetaMap *meta_ = nullptr) noexcept;
bool has_id() const noexcept;
bool params_is_object() const noexcept;
bool params_is_array() const noexcept;
const vix::json::kvs *params_object_ptr() const noexcept;
const vix::json::array_t *params_array_ptr() const noexcept;
std::string_view meta_value(std::string_view key) const noexcept;
bool has_meta(std::string_view key) const noexcept;
};Fields
| Field | Purpose |
|---|---|
method | RPC method name. |
params | Request parameters token. |
id | Optional request id token. |
transport | Optional transport label. |
meta | Optional metadata map pointer. |
Context is a lightweight view. It does not own the request payload or metadata.
Context helpers
has_id
bool has_id() const noexcept;Returns true when the request id is not null.
params_is_object
bool params_is_object() const noexcept;Returns true when params is an object.
params_is_array
bool params_is_array() const noexcept;Returns true when params is an array.
params_object_ptr
const vix::json::kvs *
params_object_ptr() const noexcept;Returns params as an object pointer, or nullptr.
params_array_ptr
const vix::json::array_t *
params_array_ptr() const noexcept;Returns params as an array pointer, or nullptr.
meta_value
std::string_view
meta_value(std::string_view key) const noexcept;Returns a metadata value, or an empty view when metadata is absent or the key is missing.
has_meta
bool has_meta(std::string_view key) const noexcept;Returns true when metadata exists and contains the key.
Example:
#include <string>
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("whoami", [](const Context &ctx) -> RpcResult
{
return obj({
"transport", std::string(ctx.transport),
"peer", std::string(ctx.meta_value("peer")),
"has_id", ctx.has_id(),
});
});
Context::MetaMap meta{
{"peer", "client-1"},
};
token request = obj({
"id", 1LL,
"method", "whoami",
});
Dispatcher dispatcher(router);
auto response = dispatcher.handle(request, "websocket", &meta);
if (response.has_value())
{
vix::print("response created");
}
return 0;
}RpcResult
RpcResult is the return type used by RPC handlers.
using RpcResult =
std::variant<vix::json::token, RpcError>;A handler returns a token for success or an RpcError for failure.
router.add("ping", [](const vix::webrpc::Context &)
-> vix::webrpc::RpcResult
{
return vix::json::obj({
"pong", true,
});
});RpcHandler
RpcHandler is the handler function signature stored by the router.
using RpcHandler =
std::function<RpcResult(const Context &)>;Handlers are synchronous and receive a read-only Context.
Router
Router stores method handlers and dispatches parsed requests.
class Router
{
public:
Router() = default;
void add(std::string name, RpcHandler handler);
bool remove(std::string_view name);
std::size_t size() const noexcept;
bool has(std::string_view name) const noexcept;
RpcResult dispatch(
const RpcRequest &req,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;
RpcResult dispatch(
const vix::json::token &raw,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;
};Router::add
Registers or replaces a method handler.
void add(std::string name, RpcHandler handler);Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("ping", [](const Context &) -> RpcResult
{
return obj({
"pong", true,
});
});
vix::print("methods:", router.size());
return 0;
}If the method already exists, the handler is replaced.
Router::remove
Removes a registered method.
bool remove(std::string_view name);Returns true when a handler was removed.
Router::size
Returns the number of registered methods.
std::size_t size() const noexcept;Router::has
Checks whether a method is registered.
bool has(std::string_view name) const noexcept;Router::dispatch with RpcRequest
Dispatches a parsed request.
RpcResult dispatch(
const RpcRequest &req,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;Behavior:
| Condition | Result |
|---|---|
| Request method is empty. | RpcError::invalid_params(...) |
| Method is not registered. | RpcError::method_not_found(...) |
| Method exists. | Handler result. |
Example:
#include <variant>
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("echo", [](const Context &ctx) -> RpcResult
{
return ctx.params;
});
RpcRequest request{
token(1LL),
"echo",
obj({
"message", "hello",
}),
};
RpcResult result = router.dispatch(request, "local");
if (std::holds_alternative<RpcError>(result))
{
const auto &error = std::get<RpcError>(result);
vix::print("error:", error.code);
return 1;
}
vix::print("dispatch succeeded");
return 0;
}Router::dispatch with token
Parses and dispatches one raw request token.
RpcResult dispatch(
const vix::json::token &raw,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;This helper supports a single request object. Use Dispatcher for batch handling, notifications, and response wrapping.
Dispatcher
Dispatcher handles the full request envelope flow.
class Dispatcher
{
public:
explicit Dispatcher(const Router &router) noexcept;
std::optional<vix::json::token> handle(
const vix::json::token &payload,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;
std::optional<RpcResponse> handle_one(
const vix::json::token &payload,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;
};The dispatcher stores a reference to the router. The router must outlive the dispatcher.
Dispatcher::handle
Handles either a single request payload or a batch payload.
std::optional<vix::json::token> handle(
const vix::json::token &payload,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;Return behavior:
| Payload | Return |
|---|---|
| Single request with id | Response object token. |
| Single notification | std::nullopt. |
| Batch with response items | Array token of response objects. |
| Batch of only notifications | std::nullopt. |
| Invalid single request | Error response token with id = null. |
| Empty batch | Error response token with id = null. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("ping", [](const Context &) -> RpcResult
{
return obj({
"pong", true,
});
});
Dispatcher dispatcher(router);
token request = obj({
"id", 1LL,
"method", "ping",
});
auto response = dispatcher.handle(request, "local");
if (response.has_value())
{
vix::print("response token created");
}
return 0;
}Dispatcher::handle_one
Handles one request object and returns an optional RpcResponse.
std::optional<RpcResponse> handle_one(
const vix::json::token &payload,
std::string_view transport = {},
const Context::MetaMap *meta = nullptr) const;Return behavior:
| Request | Return |
|---|---|
| Valid request with id | RpcResponse. |
| Notification | std::nullopt. |
| Malformed request | Error RpcResponse with id = null. |
| Handler error | Error RpcResponse with request id. |
| Handler success | Success RpcResponse with request id. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("ping", [](const Context &) -> RpcResult
{
return obj({
"pong", true,
});
});
Dispatcher dispatcher(router);
token request = obj({
"id", 1LL,
"method", "ping",
});
auto response = dispatcher.handle_one(request, "local");
if (response.has_value() && response->ok())
{
vix::print("single request succeeded");
}
return 0;
}Batch behavior
Batch handling is implemented by Dispatcher::handle(...).
Batch rules:
| Input | Behavior |
|---|---|
| Empty array | Error response with id = null. |
| Non-object item | Error response entry with id = null; processing continues. |
| Malformed request item | Error response entry with id = null; processing continues. |
| Request item with id | Response entry is added. |
| Notification item | Handler runs, but no response entry is added. |
| Batch of only notifications | std::nullopt. |
| Mixed batch | Response array contains only items that produced responses. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("ping", [](const Context &) -> RpcResult
{
return obj({
"pong", true,
});
});
token batch = array_t{
{
obj({
"id", 1LL,
"method", "ping",
}),
obj({
"method", "ping",
}),
obj({
"id", 2LL,
"method", "missing.method",
}),
},
};
Dispatcher dispatcher(router);
auto response = dispatcher.handle(batch, "local");
if (response.has_value())
{
vix::print("batch response created");
}
return 0;
}Notifications
A request without an id, or with id = null, is a notification.
Notification behavior:
| Level | Behavior |
|---|---|
| Router | Handler still executes and returns RpcResult. |
| Dispatcher | Handler executes, but no response is returned. |
| Batch | Notification is omitted from the response array. |
Example:
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("events.track", [](const Context &) -> RpcResult
{
vix::print("event handled");
return obj({
"ok", true,
});
});
token notification = obj({
"method", "events.track",
});
Dispatcher dispatcher(router);
auto response = dispatcher.handle(notification, "local");
if (!response.has_value())
{
vix::print("no response for notification");
}
return 0;
}Metadata
Metadata can be passed through Dispatcher or Router.
Context::MetaMap meta{
{"peer", "client-1"},
{"trace_id", "abc-123"},
};
auto response =
dispatcher.handle(payload, "websocket", &meta);Handlers read metadata through Context.
router.add("whoami", [](const vix::webrpc::Context &ctx)
-> vix::webrpc::RpcResult
{
return vix::json::obj({
"peer", std::string(ctx.meta_value("peer")),
"transport", std::string(ctx.transport),
});
});The metadata map is owned by the caller and must remain valid during dispatch.
Complete example
#include <variant>
#include <vix/webrpc/webrpc.hpp>
#include <vix/json/Simple.hpp>
#include <vix/print.hpp>
int main()
{
using namespace vix::json;
using namespace vix::webrpc;
Router router;
router.add("math.add", [](const Context &ctx) -> RpcResult
{
const auto *params = ctx.params_object_ptr();
if (!params)
{
return RpcError::invalid_params("params must be an object");
}
const token *a = params->get_ptr("a");
const token *b = params->get_ptr("b");
if (!a || !b || !a->is_i64() || !b->is_i64())
{
return RpcError::invalid_params("a and b must be integers");
}
return obj({
"sum", a->as_i64_or(0) + b->as_i64_or(0),
});
});
router.add("ping", [](const Context &) -> RpcResult
{
return obj({
"pong", true,
});
});
token batch = array_t{
{
obj({
"id", 1LL,
"method", "math.add",
"params", obj({
"a", 7LL,
"b", 5LL,
}),
}),
obj({
"method", "ping",
}),
obj({
"id", 2LL,
"method", "missing.method",
}),
},
};
Dispatcher dispatcher(router);
auto maybe_response = dispatcher.handle(batch, "local");
if (!maybe_response.has_value())
{
vix::print("no response");
return 0;
}
const auto *items = maybe_response->as_array_ptr().get();
if (!items)
{
vix::print("batch response must be an array");
return 1;
}
for (const auto &item : items->elems)
{
auto parsed = RpcResponse::parse(item);
if (std::holds_alternative<RpcError>(parsed))
{
vix::print("invalid response item");
continue;
}
const RpcResponse &response = std::get<RpcResponse>(parsed);
if (response.ok())
{
vix::print("success response");
}
else
{
vix::print("error response:", response.error.code);
}
}
return 0;
}This example uses the full WebRPC flow: register handlers, dispatch a batch, execute a notification, receive success and error responses, and inspect the output.
Summary
| API family | Main use |
|---|---|
RpcError | Structured RPC failure. |
RpcErrorParseResult | Explicit result for error parsing. |
RpcRequest | Request envelope for one RPC call. |
RpcResponse | Response envelope for one RPC call. |
Context | Read-only handler execution context. |
RpcResult | Handler return type: success token or error. |
RpcHandler | Handler callable stored by the router. |
Router | Method registry and synchronous dispatch. |
Dispatcher | Request parsing, batch handling, notifications, and response wrapping. |
Use Router to register and execute methods. Use Dispatcher when the input is a raw payload that needs the full WebRPC envelope flow. Use Context inside handlers to read params, id, transport, and metadata. Use RpcError for every structured RPC failure.