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.
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.
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
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.
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.
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"
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
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.
The following middleware ships with JsonApiClient
JsonApiClient.Middleware.Fuse
JsonApiClient.Middleware.StatsTracker