README.md

# Absinthe Plug

[Plug](https://hex.pm/packages/plug) support for [Absinthe](https://hex.pm/packages/absinthe),
an experimental GraphQL API toolkit.

## Installation

Install from [Hex.pm](https://hex.pm/packages/absinthe_plug):

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

Add it to your `applications` configuration in `mix.exs`:

```elixir
def application do
  [applications: [:absinthe_plug]]
end
```

`AbsinthePlug` also requires a JSON codec. Poison works out of the box.

```elixir
def deps do
  [
    {:absinthe_plug, "~> 0.1.2"},
    {:poison, "~> 1.3.0"}
  ]
end
```

## Example

Assuming the following [Absinthe schema](http://hexdocs.pm/absinthe/Absinthe.Schema.html):

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema
  alias Absinthe.Type

  # Example data
  @items %{
    "foo" => %{id: "foo", name: "Foo"},
    "bar" => %{id: "bar", name: "Bar"}
  }

  def query do
    %Type.Object{
      fields: fields(
        item: [
          type: :item,
          args: args(
            id: [type: non_null(:string)]
          ),
          resolve: fn %{id: item_id}, _ ->
            {:ok, @items[item_id]}
          end
        ]
      )
    }
  end

  @absinthe :type
  def item do
    %Type.Object{
      description: "An item",
      fields: fields(
        id: [type: :id],
        name: [type: :string]
      )
    }
  end

end

```

And the following plug configuration:

```elixir
  plug AbsinthePlug,
    schema: MyApp.Schema
```

We could retrieve the name of the `"foo"` item with this query document:

```
query GetItem($id: ID!) {
  item(id: $id) {
    name
  }
}
```

As long as we pass in `"foo"` for the `id` variable. This would be the result,
in JSON:

```json
{
  "data": {
    "item": {
      "name": "Foo"
    }
  }
}
```

The plug supports making the request a number of ways:

### Via a GET

With a query string:

```
?query=query+GetItem($id:ID!){item(id:$id){name}}&variables={id:"foo"}
```

Due to [varying limits on the maximum size of URLs](http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers), we recommend using one of the POST options below instead, putting the `query` into the body of the request.

### Via an `application/json` POST

With a POST body:

```
{
  "query": "query GetItem($id: ID!) { item(id: $id) { name } }",
  "variables": {
    "id": "foo"
  }
}
```

(We could also pull either `query` or `variables` out to the query string, just
as in the [GET example](./README.md#via-a-get).)

### Via an `application/graphql` POST

With a query string:

`?variables={id:"foo"}`

And a POST body:

```
query GetItem($id: ID!) {
  item(id: $id) {
    name
  }
}
```

For more information on how requests are handled, see [HTTP API](./README.md#http-api), below.

## Configuration

As a plug, `AbsinthePlug` requires very little configuration. If you want to support
`application/x-www-form-urlencoded` or `application/json` you'll need to plug
`Plug.Parsers` first.

Here is an example plug module.

```elixir
plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  pass: ["*/*"],
  json_decoder: Poison

plug AbsinthePlug,
  schema: MyApp.Linen.Schema,
  adapter: Absinthe.Adapter.LanguageConventions
```

`AbsinthePlug` requires a `schema:` config. The `LanguageConventions` adapter
allows you to use `snake_case_names` in your schema while still accepting and
returning `camelCaseNames`.

It also takes several options. See [the documentation](https://hexdocs.pm/absinthe_plug/AbsinthePlug.html#init/1)
for the full listing.

## From Phoenix

Here is an example [Phoenix](https://hex.pm/packages/phoenix) endpoint that uses `AbsinthePlug`

```elixir
defmodule MyApp.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  if code_reloading? do
    plug Phoenix.CodeReloader
  end

  plug Plug.RequestId
  plug Plug.Logger

  # other standard phoenix plugs go here

  plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Poison

  plug AbsinthePlug,
    schema: MyApp.Schema,
    adapter: Absinthe.Adapter.LanguageConventions
end
```

If you still want to use your Phoenix router, you need to add a `:path` option
so it will know which path to respond to. All other paths will be
passed along to plugs farther down the line such as a Phoenix router.

```elixir
plug AbsinthePlug,
  schema: MyApp.Schema,
  adapter: Absinthe.Adapter.LanguageConventions,
  path: "/api"
```

### HTTP API

How clients interact with the plug over HTTP is designed to closely match that
of the official
[express-graphql](https://github.com/graphql/express-graphql) middleware.

In the [example above](./README.md#example), we went over the various ways to
make a request, but here are the details:

Once installed at a path, the plug will accept requests with the
following parameters:

  * `query` - A string GraphQL document to be executed.

  * `variables` - The runtime values to use for any GraphQL query variables
    as a JSON object.

  * `operationName` - If the provided `query` contains multiple named
    operations, this specifies which operation should be executed. If not
    provided, a 400 error will be returned if the `query` contains multiple
    named operations.

The plug will first look for each parameter in the query string, eg:

```
/graphql?query=query+getUser($id:ID){user(id:$id){name}}&variables={"id":"4"}
```

If not found in the query string, it will look in the POST request body, using
a strategy based on the `Content-Type` header.

For content types `application/json` and `application/x-www-form-urlencoded`,
configure `Plug.Parsers` (or equivalent) to parse the request body before `AbsinthePlug`, eg:

```elixir
plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  pass: ["*/*"],
  json_decoder: Poison
```

For `application/graphql`, the POST body will be parsed as GraphQL query string,
which provides the `query` parameter. If `variables` or `operationName` are
needed, they should be passed as part of the

## Roadmap & Contributions

For a list of specific planned features and version targets, see the
[milestone list](https://github.com/CargoSense/absinthe_plug/milestones).

(If your issue is Absinthe (not Plug) related, please see the
[Absinthe](https://github.com/CargoSense/absinthe) project.)

We welcome issues and pull requests; please see [CONTRIBUTING](./CONTRIBUTING.md).

## License

BSD License

Copyright (c) CargoSense, Inc.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

 * Neither the name Facebook nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific
   prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.