README.md

# JSON-RPC 2.0 plug

`JSONRPC2Plug` is an Elixir library for a JSON-RPC 2.0 server. Can be used as the plug middleware or as a standalone transport-agnostic server handler.

## Installation

The package can be installed by adding `jsonrpc2_plug` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:jsonrpc2_plug, "~> 0.1.0"}
  ]
end
```

## Usage

### Defining Services

Services should use `JSONRPC2Plug.Service`, which allows describing methods of service. Each method is a module which use `JSONRPC2Plug.Method`.

Examples:


```elixir
defmodule CalculatorService do
  use JSONRPC2Plug.Service

  method "add", AddMethod
  method "subtract", SubtractMethod
  method "multiply", MultiplyMethod
  method "divide", DivideMethod
end

defmodule AddMethod do
  use JSONRPC2Plug.Method
end
# and so on...
```

### Defining Methods

There are two possible ways to execute a request: `call` and `cast`. The first assumes the response which the service will return, the second does not. The module should implement at least one `handle_call` or `handle_cast` callback function to handle requests.

```elixir
defmodule AddMethod do
  use JSONRPC2Plug.Method

  # It handles requests like this:
  # {"id": "123", "method": "add", "params": {"x": 10, "y": 20}, "jsonrpc": "2.0"}
  def handle_call(%{"x" = > x, "y" => y}, _conn) do
    {:ok, x + y}
  end
end
```

The first argument is the `"params"` data comes from request JSON. According to [JSONRPC2 spec](https://www.jsonrpc.org/specification), it must be either object or an array of arguments.

The second argument is the `Plug.Conn` struct. Sometimes it could be useful to access the `Plug` connection.

The module implements behaviour `JSONRPC2Plug.Method` which consists of five callbacks: `handle_call`, `handle_cast`, `validate`, `handle_error` and, `handle_exception`.

#### `handle_call` and `handle_cast`

_TODO: Add description_

#### `validate`

This function is for the validation of the input dataset.

```elixir
import JSONRPC2Plug.Validator, only: [type: 1, required: 0]

def validate(params) do
  params
  |> Validator.validate("x", [type(:integer), required()])
  |> Validator.validate("y", [type(:integer), required()])
  |> Validator.unwrap()
end
```

The library has its own validator. It has 8 built-in validations: `type`, `required`, `not_empty`, `exclude`, `include`, `len`, `number` and `format`. However, you can write custom validations and extend existing ones.

Moreover, you can use any preferred validator (eg. [`valdi`](https://github.com/bluzky/valdi)), but you should respect the following requirements: the `validate` function should return either `{:ok, params}` or `{:invalid, errors}`. Where `errors` could be any type that can be safely encoded to JSON and `params` is params to pass into `handle_call` or `handle_cast` functions.

#### `handle_error` and `handle_exception`

_TODO: Add description_

### Add as a plug to the router

_TODO: Add description_

### Using as standalone module

_TODO: Add description_

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/jsonrpc2_plug](https://hexdocs.pm/jsonrpc2_plug).