== JSON-RPC

Jooby provides full support for the JSON-RPC 2.0 specification, allowing you to build robust RPC APIs using standard Java and Kotlin controllers.

The implementation leverages Jooby's Annotation Processing Tool (APT) to generate highly optimized, reflection-free dispatchers at compile time.

=== Usage

Add dependencies:

[dependency, artifactId="jooby-jackson3:Jackson module, jooby-jsonrpc-jackson3:Jackson JSON-RPC implementation, jooby-jsonrpc:JSON-RPC Module"]
.

To expose a JSON-RPC endpoint, annotate your controller or service with `@JsonRpc`. You can optionally provide a namespace to the annotation, which will prefix all generated method names for that class.

.JSON-RPC
[source,java,role="primary"]
----
import io.jooby.rpc.jsonrpc.JsonRpc;

@JsonRpc("movies")
public class MovieService {

  public Movie getById(int id) {
    return database.stream()
      .filter(m -> m.id() == id)
      .findFirst()
      .orElseThrow(() -> new NotFoundException("Movie not found: " + id));
  }
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.rpc.jsonrpc.JsonRpc

@JsonRpc("movies")
class MovieService {

  fun getById(id: Int): Movie {
    return database.asSequence()
      .filter { it.id == id }
      .firstOrNull() ?: throw NotFoundException("Movie not found: $id")
  }
}
----

When the Jooby APT detects the `@JsonRpc` annotation, it generates a class ending in `Rpc_` (e.g., `MovieServiceRpc_`) that implements the `io.jooby.rpc.jsonrpc.JsonRpcService` interface.

The annotation dictates how the protocol methods are named and exposed:

* **Class Level:** Placing `@JsonRpc` on a class treats its public methods as JSON-RPC endpoints. An optional string value defines a namespace prefix for all methods within that class (e.g., `@JsonRpc("movies")`). If no value is provided, no namespace is applied.
* **Method Level:** By default, the generated JSON-RPC method name is exactly the name of the Java/Kotlin method, prefixed by the class-level namespace if one is present (e.g., `movies.getById` or just `getById` if no namespace was set). You can also place `@JsonRpc("customName")` directly on specific methods to explicitly override this default naming convention.

**Mixing Annotations:** You can freely mix standard REST annotations (like `@GET`, `@POST`) and `@JsonRpc` on the same class. The APT handles this by generating two entirely separate dispatchers: one standard MVC extension (e.g., `MovieService_`) and one JSON-RPC service (e.g., `MovieServiceRpc_`). They do not interfere with each other, allowing you to expose the exact same business logic over both REST and JSON-RPC simultaneously.

=== Registration

Register the generated `JsonRpcService` in your application using the `jsonrpc` method. You must also install a supported JSON engine.

.JSON-RPC
[source,java,role="primary"]
----
import io.jooby.Jooby;
import io.jooby.jackson.Jackson3Module;

{
  install(new Jackson3Module());                      // <1>

  install(new JsonRpcJackson3Module());               // <2>

  install(new JsonRpcModule(new MovieServiceRpc_())); // <3>
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.kt.Kooby
import io.jooby.jackson.Jackson3Module

{
  install(Jackson3Module())                           // <1>

  install(JsonRpcJackson3Module())                    // <2>

  install(JsonRpcModule(MovieServiceRpc_()))          // <3>
}
----

1. Install a JSON engine
2. Install a JSON-RPC implementation
3. Register the generated JSON-RPC service

=== JSON Engine Support

The JSON-RPC extension delegates payload parsing and serialization to Jooby's standard JSON modules while enforcing strict JSON-RPC 2.0 compliance (such as the mutual exclusivity of `result` and `error` fields).

Supported engines include:

* **Jackson 2**: `Jackson2Module`, `JsonRpcJackson2Module`
* **Jackson 3**: `Jackson3Module`, `JsonRpcJackson3Module`
* **Avaje JSON-B**: `AvajeJsonbModule`, `JsonRpcAvajeJsonbModule`

No additional configuration is required. The generated dispatcher automatically hooks into the installed engine using the `JsonRpcParser` and `JsonRpcDecoder` interfaces, ensuring primitive types are strictly validated and parsed.

=== Error Mapping

Jooby seamlessly bridges standard Java application exceptions and HTTP status codes into the JSON-RPC 2.0 format using the `JsonRpcErrorCode` mapping. You do not need to throw custom protocol exceptions for standard failures.

When an application exception is thrown (like a `NotFoundException` with an HTTP 404 status), the dispatcher catches it and translates it into a compliant JSON-RPC error. The standard HTTP status defines the error `code`, while the specific exception message is safely passed into the `data` field:

[source,json]
----
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32004,
    "message": "Not found",
    "data": "Movie not found: 99"
  },
  "id": 1
}
----

==== Standard Protocol Errors

The engine handles core JSON-RPC 2.0 protocol errors automatically, returning HTTP 200 OK with the corresponding error payload:

* `-32700`: Parse error (Malformed JSON)
* `-32600`: Invalid Request (Missing version, ID, or malformed envelope)
* `-32601`: Method not found
* `-32602`: Invalid params (Missing arguments, type mismatches)
* `-32603`: Internal error

Application-defined errors map standard HTTP status codes to the `-32000` to `-32099` range (e.g., an HTTP 404 maps to `-32004`, an HTTP 401 maps to `-32001`).

=== Batch Processing

Batch processing is natively supported. Clients can send an array of JSON-RPC request objects, and the dispatcher will process them and return an array of corresponding response objects.

In accordance with the specification, notifications (requests lacking an `id` field) are processed normally but generate no response payload, leaving no trace in the returned batch array.
