/json_api_client

JSON API Client Library For Elixir

Primary LanguageElixirMIT LicenseMIT

JsonApiClient

Hex.pm Build Docs

A JSON API Client for elixir.

NOTICE: This library is new and in active development. There could be backwards incompatable changes as the design shakes out. YMMV, PRs welcome.

Installation

This package can be installed by adding json_api_client to your list of dependencies in mix.exs:

def deps do
  [
    {:json_api_client, "~> 1.0.0"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/json_api_client.

Usage

import JsonApiClient.Request

base_url = "http://example.com/"

# Fetch a resource by URL
{:ok, response} = fetch Request.new(base_url <> "/articles/123")

# build the request by composing helper functions
{:ok, response} = Request.new(base_url <> "/articles")
|> id("123")
|> fetch

# Fetch a list of resources
{:ok, response} = Request.new(base_url <> "/articles")
|> fields(articles: "title,topic", authors: "first-name,last-name,twitter")
|> include(:author)
|> sort(:id)
|> page(size: 10, number: 1)
|> filter(published: true)
|> params(custom1: 1, custom2: 2)
|> fetch

# Delete a resource
{:ok, response} = Request.new(base_url <> "/articles")
|> id("123")
|> delete

# Create a resource
new_article = %Resource{
  type: "articles",
  attributes: %{
    title: "JSON API paints my bikeshed!",
  }
}
{:ok, %{status: 201, doc: %{data: article}}} = Request.new(base_url <> "/articles")
|> resource(new_article)
|> create

# Update a resource
{:ok, %{status: 200, doc: %{data: updated_article}}} = Request.new(base_url <> "/articles")
|> resource(%Resource{article | attributes: %{title: "New Title}})
|> update

Non-compliant servers

For the most part this library assumes that the server you're talking to implements the JSON:API spec correctly and treats deviations from that spec as exceptional (causing JsonApiClient.execute/1 to return an {:error, _} tuple for example). One exception to this rule is the case where a server sends back an invalid body (HTML or some non-json string) along with a 4** or 5** status code. In those cases the body will simple be ignored. See the docs for JsonApiClient.execute/1 for more details.

Helpers for common URI structures

The JSON:API specification doesn't provide any guidance on URI structure, but there is a common convention for REST apis to expose an enpoints with the following structure

# fetch a list of resources of a given type
GET /:type_name

# Create a resource of a given type
POST /:type_name

# Fetch/Update/Delete a resource by id
GET /:type_name/:id
PATCH /:type_name/:id
DELETE /:type_name/:id

When making requests to API endpoints that follow these conventions you can avoid having to build the full path yourself by adding JsonApiClient.Resource to the request.

# GET base_url <> "/articles/123"
{:ok, response} = Request.new(base_url)
|> resource(%Resource{id: "123", type: "articles"})
|> fetch

# GET base_url <> "/articles"
{:ok, response} = Request.new(base_url)
|> resource(%Resource{type: "articles"})
|> fetch

You can also build paths to nested resources by passing a Resource to path/2

# GET base_url <> "/articles/123/comments/456"
{:ok, response} = Request.new(base_url)
|> path(%Resource{id: "123", type: "articles"})
|> resource(%Resource{id: "456", type: "comments"})
|> fetch

If the API your making requests of follows a different URI pattern you can pass a string to path/2 and it will be appended to the base url.

Configuration

user agent suffix

Every request made carries a special User-Agent header that looks like: json_api_client/1.0.0/user_agent_suffix. Each client is expected to set its user_agent_suffix via:

config :json_api_client, user_agent_suffix: "yourSufix"

timeout

This library allows its users to specify a timeout for all its service calls by using a timeout setting. By default, the timeout is set to 500msecs.

config :json_api_client, timeout: 200

middlewares

JsonApiClient is implemented using a middleware architecture. You can configure the middleware stack by setting middlewares to a list of {Module, opts} tuples where Module is a module that implements the JsonApiClient.Middleware behavior and opts is the options that will be passed as the last argument to the modules call function.

config :json_api_client,
  middlewares: [
    {JsonApiClient.Middleware.DocumentParser, nil}
    {JsonApiClient.Middleware.HTTPClient, nil},
  ]

If you don't configure a value for middlewares you'll get a stack equivilent to the one configured in the preceding example.

included middlewre

The following middleware ships with JsonApiClient:

  • JsonApiClient.Middleware.Fuse
  • JsonApiClient.Middleware.StatsTracker

Please refer to the module documentation for each middleware for information