Writing Schemas
A GraphQL API starts by building a schema. Using Absinthe, schemas are normal
modules that use Absinthe.Schema
.
Here's a schema that supports looking up an item by ID:
# filename: myapp/schema.ex
defmodule MyAppWeb.Schema do
use Absinthe.Schema
# Example data
@items %{
"foo" => %{id: "foo", name: "Foo"},
"bar" => %{id: "bar", name: "Bar"}
}
query do
field :item, :item do
arg :id, non_null(:id)
resolve fn %{id: item_id}, _ ->
{:ok, @items[item_id]}
end
end
end
end
You may want to refer to the <a href="https://hexdocs.pm/absinthe/">Absinthe API documentation</a> for more detailed information as you look this over..
Some macros and functions used here that are worth mentioning, pulled in automatically from
Absinthe.Schema.Notation
by use Absinthe.Schema
:
query
- Defines the root query object. It's like usingobject
but with nice defaults. There is a matchingmutation
macro as well.field
- Defines a field in the enclosingobject
,input_object
, orinterface
.arg
- Defines an argument in the enclosingfield
ordirective
.resolve
- Sets the resolve function for the enclosingfield
.
You'll notice we mention some types being referenced: :item
and :id
. :id
is a built-in scalar type (like :string
, :boolean
, and others), but :item
we need to define ourselves.
We can do it in the same MyAppWeb.Schema
module, using the object
macro defined by Absinthe.Schema.Notation
:
# filename: myapp/schema.ex
@desc "An item"
object :item do
field :id, :id
field :name, :string
end
Now you can use Absinthe to execute a query document. Keep in mind that for
HTTP, you'll probably want to use
Absinthe.Plug instead of executing
GraphQL query documents yourself. Absinthe doesn't know or care about HTTP,
but the absinthe_plug
project does: it handles the vagaries of interacting
with HTTP GraphQL clients so you don't have to.
If you were executing query documents yourself (let's assume for a local tool), it would go something like this:
"""
{
item(id: "foo") {
name
}
}
"""
|> Absinthe.run(MyAppWeb.Schema)
# Result
{:ok, %{data: %{"item" => %{"name" => "Foo"}}}}
Your schemas can be further customized using the options available to
Absinthe.Schema.Notation.field/4
to help provide for a richer experience for
your users, customize the field names, or mark fields as deprecated.
# filename: myapp/language_schema.ex
@desc "A Language"
object :language do
field :id, :id
field :iso_639_1, :string, description: "2 character ISO 639-1 code", name: "iso639"
field :name, :string, description: "English name of the language"
end
Importing Types
We could also move our type definitions out into a different module, for instance, MyAppWeb.Schema.Types
, and then use import_types
in our MyAppWeb.Schema
:
# filename: myapp/schema/types.ex
defmodule MyAppWeb.Schema.Types do
use Absinthe.Schema.Notation
object :item do
field :id, :id
field :name, :string
end
# ...
end
# filename: myapp/schema.ex
defmodule MyAppWeb.Schema do
use Absinthe.Schema
import_types MyAppWeb.Schema.Types
# ...
end
It's a nice way of separating the top-level query
and mutation
information,
which define the surface area of the API, with the actual types that it uses.
See Importing Types for a full guide to importing types.