Creating an API

The @JsonRPCApi Annotation

Annotate any class with @JsonRPCApi to expose its public methods as JSON-RPC endpoints:

import io.quarkiverse.jsonrpc.api.JsonRPCApi;

@JsonRPCApi
public class GreetingService {

    public String hello() {
        return "Hello";
    }

    public String hello(String name) {
        return "Hello " + name;
    }
}

The extension discovers annotated classes at build time. Each public method becomes a JSON-RPC method.

Method Names

Methods are addressed using the format ClassName#methodName. For the example above, clients call:

  • GreetingService#hello (no params) — calls hello()

  • GreetingService#hello (with a name param) — calls hello(String name)

The framework disambiguates overloaded methods by the number and names of the parameters provided.

Custom Scope

By default, the class simple name is used as the scope prefix. You can provide a custom scope:

@JsonRPCApi("greet")
public class GreetingService {

    public String hello(String name) {
        return "Hello " + name;
    }
}

Now the method is called as greet#hello instead of GreetingService#hello. This is useful for versioning or logical grouping.

Method Discovery Rules

The following rules apply when the extension scans your class:

  • Only public methods are exposed.

  • Constructors and static methods are ignored.

  • Private and package-private methods are ignored.

  • Methods annotated with @JsonRPCIgnore are excluded (see Ignoring Methods below).

  • Methods may return any type, including void (see Void Methods (Fire-and-Forget) below).

Ignoring Methods

Annotate a public method with @JsonRPCIgnore to exclude it from JSON-RPC registration. The method remains public and usable from Java (for example, by CDI or other beans), but it will not be exposed as a JSON-RPC endpoint and will not appear in the generated JavaScript client proxy:

import io.quarkiverse.jsonrpc.api.JsonRPCApi;
import io.quarkiverse.jsonrpc.api.JsonRPCIgnore;

@JsonRPCApi
public class OrderService {

    public Order getOrder(String id) {
        return findOrder(id);
    }

    @JsonRPCIgnore
    public void recalculateTotals() {
        // Public for CDI, but not a JSON-RPC endpoint
    }
}

CDI Integration

All @JsonRPCApi classes are automatically registered as @Singleton CDI beans. You can inject any CDI bean:

@JsonRPCApi
public class OrderService {

    @Inject
    OrderRepository repository;

    public Order getOrder(String id) {
        return repository.findById(id);
    }
}

Void Methods (Fire-and-Forget)

Methods returning void are supported for fire-and-forget operations. They pair naturally with JSON-RPC notifications — requests sent without an id:

@JsonRPCApi
public class EventService {

    public void trackEvent(String name) {
        // process the event — no result to return
    }
}

The client sends a notification (no id field):

{
  "jsonrpc": "2.0",
  "method": "EventService#trackEvent",
  "params": { "name": "page_view" }
}

The server executes the method and sends no response.

If the client does include an id, the server responds with "result": null per the JSON-RPC 2.0 specification:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": null
}

Void methods follow the same execution mode rules as any other method — they are blocking by default and can be annotated with @NonBlocking or @RunOnVirtualThread. See Execution Modes & Return Types.

Custom Path

By default, all @JsonRPCApi classes share the global WebSocket endpoint (see WebSocket Endpoint below). You can assign a class to a separate WebSocket path using the path attribute:

@JsonRPCApi(path = "/admin-rpc")
public class AdminService {

    public String getStatus() {
        return "OK";
    }
}

This registers an additional WebSocket route at /admin-rpc. The methods on AdminService are only accessible through the custom path — clients connected to the default global endpoint will receive a "method not found" error if they try to call AdminService methods.

You can combine path with a custom scope:

@JsonRPCApi(value = "admin", path = "/admin-rpc")
public class AdminService {

    public String getStatus() {
        return "OK";
    }
}

Now the method is called as admin#getStatus via the /admin-rpc WebSocket endpoint.

Multiple API Groups

Different classes can use different paths, enabling logical separation:

@JsonRPCApi(path = "/admin-rpc")
public class AdminService {
    public String getStatus() { ... }
}

@JsonRPCApi(path = "/public-rpc")
public class PublicService {
    public String getInfo() { ... }
}

@JsonRPCApi
public class DefaultService {
    public String hello() { ... } (1)
}
1 No path — served on the default global endpoint (/json-rpc).

This is useful for applying different security policies per path. See Security — Per-Path Security for details.

WebSocket Endpoint

By default, all JSON-RPC methods are served through a WebSocket endpoint at:

ws://localhost:8080/json-rpc

Classes with a path attribute register additional WebSocket routes at their specified paths and are only callable from those paths. Classes without a path are served exclusively on the default global endpoint.

See Configuration Reference to change the default path.

JSON-RPC 2.0 Protocol

Requests follow the JSON-RPC 2.0 specification:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "GreetingService#hello",
  "params": { "name": "World" }
}

Successful responses:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Hello World"
}

Error responses:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}

Standard JSON-RPC 2.0 error codes are used:

Code Meaning

-32700

Parse error

-32600

Invalid request

-32601

Method not found

-32602

Invalid params

-32603

Internal error

-32000

Unauthorized (authentication required)

-32001

Forbidden (insufficient permissions)