README.md

# JSON:API library for Plug and Phoenix applications

Server library to build [JSON:API](http://jsonapi.org) compliant REST APIs.

## JSON:API Support

This library currently implements version `1.0` of the [JSON:API](https://jsonapi.org) specification.

## Documentation

- [Full docs here](https://hexdocs.pm/jsonapi_plug)
- [JSON API Specification (v1.0)](https://jsonapi.org/format/1.0/)

## Quickstart

### Installation

Add the following line to your `mix.deps` file with the desired version to install `jsonapi_plug`.

```elixir
defp deps do [
  ...
  {:jsonapi_plug, "~> 1.0"}
  ...
]
```

### Configuration

You start by declaring one or more APIs. APIs are collections of endpoints that
share a common configuration:

```elixir
defmodule MyApp.API do
  use JSONAPIPlug.API, otp_app: :my_app
end
```

See the `JSONAPIPlug.API` module documentation to learn how to customize your APIs
via application configuration of your app.

### Receiving requests

In order to parse `JSON:API` requests from clients you need to add the `JSONAPIPlug.Plug` plug to each of your plug pipelines or phoenix controllers handling requests for a specific resource:

```elixir
defmodule MyApp.PostsController do
  ...
  plug JSONAPIPlug.Plug, api: MyApp.API, resource: MyApp.PostResource
  ...
end
```

This will take care of ensuring `JSON:API` specification compliance and will return errors for invalid requests.

The `:api` option expects a module using `JSONAPI.API` for configuration.

The `:resource` option expects a module using `JSONAPIPlug.Resource` to convert to/from `JSON:API` format.

When requests are processed, the `:jsonapi_plug` connection private field is populated with the parsed request.

See the `JSONAPIPlug.Plug` module documentation for usage and options.

### Serving responses

To start serving responses, you need to have some data to return to clients:

```elixir
defmodule MyApp.Post do
  @type t :: %__MODULE__{id: pos_integer(), body: String.t(), title: String.t()}

  @enforce_keys [:id, :body, :title]
  defstruct [:id, :body, :title]
end
```

and define a resource module to render your resource:

```elixir
defmodule MyApp.PostResource do
  use JSONAPIPlug.Resource,
    type: "post",
    attributes: [
      title: nil,
      text: nil,
      excerpt: [serialize: fn %Post{} = post, _conn -> String.slice(post.body, 0..5) end]
    ]

  @impl JSONAPIPlug.Resource
  def meta(%Post{} = post, _conn), do: %{slug: to_slug(post.title)}
end
```

To use the resource module in Phoenix, just call render and pass the data from your controller:

```elixir
  defmodule MyAppWeb.PostsController do
    ...
    plug JSONAPIPlug.Plug, api: MyApp.API, resource: MyApp.PostResource
    ...

    def create(%Conn{private: %{jsonapi_plug: jsonapi_plug}} = conn, params) do
      post = ...create a post using jsonapi_plug parsed parameters...
      render(conn, "create.json", %{data: post})
    end

    def index(%Conn{private: %{jsonapi_plug: jsonapi_plug}} = conn, _params) do
      posts = ...load data using jsonapi_plug parsed parameters...
      render(conn, "index.json", %{data: posts})
    end

    def show(%Conn{private: %{jsonapi_plug: jsonapi_plug} = conn, _params) do
      post = ...load data using jsonapi_plug parsed parameters...
      render(conn, "show.json", %{data: post})
    end

    def udate(%Conn{private: %{jsonapi_plug: jsonapi_plug}} = conn, params) do
      post = ...update a post using jsonapi_plug parsed parameters...
     render(conn, "update.json", %{data: post})
    end
  end
```

If you have a `Plug` application, you can call `JSONAPIPlug.Resource.render/5` to generate a `JSONAPI.Document` with your data for the client. The structure is serializable to JSON with `Jason`.

See the `JSONAPIPlug.Plug` and `JSONAPIPlug.Resource` modules documentation for more information.

## Contributing

- This project was born as a fork of the [jsonapi](https://github.com/beam-community/jsonapi)
library but has since been completely rewritten and is now a completely different project.
- PRs for new features, bug fixes, documentation and tests are welcome
- If you are proposing a large feature or change, please open an issue for discussion