Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lucacorti/jsonapi_plug

JSON:API library for Plug and Phoenix applications
https://github.com/lucacorti/jsonapi_plug

elixir elixir-lang elixir-library elixir-phoenix json-api

Last synced: 6 days ago
JSON representation

JSON:API library for Plug and Phoenix applications

Awesome Lists containing this project

README

        

# 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][json:api] specification.
However, to support resource creation alongside included resources, `JSON:API 1.1` `lid` is supported.

## Documentation

- Full documentation can be found on [hexdocs.pm][jsonapi_plug-hexdocs]

For the `JSON:API` specification documentation as well as other client and server implementations:

- [JSON:API][json:api]
- [JSON:API Specification (v1.0)][json:api-1.0]
- [JSON:API Specification (v1.1)][json:api-1.1]

## 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, "~> 2.0"}
...
]
```

### Upgrading

Upgrading the library should be safe for minor and patch vesion upgrades, it's still a good practice
to check the [changelog][changelog] for more information on the release content. For major version
upgrades, breaking changes should be expected, and the [upgrade guide][upgrade] contains instructions
on upgrading between major releases of the library.

### 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.

### Resources

To start accept requests and serving responses, you need to define `JSON:API` resources.
Resources can be any struct `@derive`-ing the `JSONAPIPlug.Resource` protocol:

```elixir
defmodule MyApp.Post do
use Ecto.Schema

@type t :: %__MODULE__{id: pos_integer(), body: String.t(), title: String.t()}

@derive {
JSONAPIPlug.Resource,
type: "post",
attributes: [:title, :text, :excerpt]
}
schema "posts" do
field :title
field :text
end

...
end
```

See `JSONAPIPlug.Resource` for the complete documentation of options you can pass to `@derive`,
including how to control serialization and deserialization, and add related resources.

Also, three optional protocols are available to allow further customization of resources.

- Resource attribute custom serialization and deserialization: `JSONAPIPlug.Resource.Attribute`
- Resource link generation: `JSONAPIPlug.Resource.Links`
- Resource link generation: `JSONAPIPlug.Resource.Meta`

### Usage with Phoenix

To serve JSON:API resources in Phoenix, you need to define routes in your router:

```elixir
defmodule MyAppWeb.Router do
...
resource "/posts", MyApp.PostsController, only: [:create, :index, :show]
patch "/posts/:id", MyApp.PostsController, :update
end
```

In order to parse `JSON:API` requests from clients you need to add the `JSONAPIPlug.Plug` plug to each of your
phoenix controllers handling requests for a specific resource. This will take care of ensuring `JSON:API` request
compliance and will return errors for malformed requests.

When a valid request processed, the `:jsonapi_plug` `Plug.Conn` private field will be populated in the controller.

You can learn all about advanced request handling, including custom filtering, relationships inclusion, pagination
and sparse fields support by reading the `JSONAPIPlug.Plug` module documentation.

Once you receive a request in your controller and load data, you just call render to send a response:

```elixir
defmodule MyAppWeb.PostsController do
...
plug JSONAPIPlug.Plug, api: MyApp.API, path: "posts", resource: MyApp.Post
...

def create(%Conn{private: %{jsonapi_plug: %JSONAPIPlug{} = 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: %JSONAPIPlug{} = jsonapi_plug}} = conn, _params) do
posts = ...load data using jsonapi_plug parsed parameters...
render(conn, "index.json", %{data: post})
end

def show(%Conn{private: %{jsonapi_plug: %JSONAPIPlug{} = 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: %JSONAPIPlug{} = jsonapi_plug}} = conn, params) do
post = ...update a post using jsonapi_plug parsed parameters...
render(conn, "update.json", %{data: post})
end
end
```

For phoenix to dispatch this for rendering you need to add this code to your `MyAppWeb` module:

```elixir
def MyAppWeb do
...

def json_api do
quote do
use JSONAPIPlug.Phoenix.Component
end
end

...
end
```

and define a corresponding rendering template module:

```elixir
defmodule SibillWeb.PostsJSON do
@moduledoc false

use MyAppWeb, :json_api
end
```

alternatively you can skip these steps by calling `JSONAPIPlug.render/4` directly instead of `render/3` in you controller:

```elixir
...
def show(%Conn{private: %{jsonapi_plug: %JSONAPIPlug{} = jsonapi_plug}} = conn, _params) do
post = ...load data using jsonapi_plug parsed parameters...
JSONAPIPlug.render(conn, post \\ nil, meta \\ nil, options \\ [])
end
...
```

### Usage with Plug

If you have a `Plug` application, assuming you already set up routing you can call `JSONAPIPlug.render/4` in your
pipeline to generate a `JSONAPI.Document` with your data for the client.

```elixir
JSONAPIPlug.render(conn, post)
|> Jason.encode!()
```

Render returns a `JSONAPI.Document`, that is serializable to JSON via `Jason`.

## Contributing

- This project was born as a fork of the [jsonapi][jsonapi-fork]
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

[changelog]: https://hexdocs.pm/jsonapi_plug/changelog.html
[json:api-1.0]: https://jsonapi.org/format/1.0/
[json:api-1.1]: https://jsonapi.org/format/1.1/
[json:api]: https://jsonapi.org
[jsonapi-fork]: https://github.com/beam-community/jsonapi
[jsonapi_plug-hexdocs]: https://hexdocs.pm/jsonapi_plug
[upgrade]: https://hexdocs.pm/jsonapi_plug/upgrading.html