Our Schema Module

To build a GraphQL API, you’ll need to construct a GraphQL schema, which defines its domain model and how data is retrieved. That can seem pretty overwhelming at first, so we’ll help you start by showing you where to put the schema and ease you into your first query.

We want this schema to support clients of our API accessing a list of menu items. An example query from a client might look something like this:

 {
  menuItems {
  name
  }
 }

If the user was talking to us, this would translate to “Give me the names of all of the menu items that you have.” Here’s a sample of the type of JSON they’d expect in response:

 {
 "data"​: {
 "menuItems"​: [
  {​"name"​: ​"Reuben"​},
  {​"name"​: ​"Croque Monsieur"​},
  {​"name"​: ​"Muffuletta"​}
  ]
  }
 }

To make this happen, we need to define a GraphQL object type for our menu items.

Defining an Object Type

All data in GraphQL has an associated type. Our menu item type is what GraphQL refers to as an object, since it contains a number of fields (like name). Let’s define our menu item object type so that clients can retrieve information about the structure of menu items. We do this using the object macro that using Absinthe.Schema gives us.

Your Schema in an Abstraction Layer

images/aside-icons/note.png

Keep in mind that your API and the underlying data representations do not need to be identical, or even have the same structure. One of the main values in modeling GraphQL types is that they can serve as an abstraction over whatever backing data store (or service) contains the associated data, transforming it before it’s transmitted to API users.

Here are the beginnings of the schema module, with the boilerplate for our new menu item type. You can see we’re also stubbing out the query definition for now (more on that soon):

 defmodule​ PlateSlateWeb.Schema ​do
 use​ Absinthe.Schema
 
  query ​do
 # <<Ignore this for now>>
 end
 
  object ​:menu_item​ ​do
 # <<We'll add fields soon>>
 end
 end

Before we add any fields, let’s fire up Interactive Elixir from the root directory of our application:

 $ ​​iex​​ ​​-S​​ ​​mix
 iex(1)>

Don’t Forget -S mix

images/aside-icons/warning.png

It’s important that you run iex with the -S mix option. Without it, your application code won’t be automatically compiled, started, and available to you from your session.

Once the prompt shows up, let’s take a look at how the menu item object type is modeled in our schema module. We’ll use the handy Absinthe.Schema.lookup_type function:

 iex(1)>​ Absinthe.Schema.lookup_type(PlateSlateWeb.Schema, ​"​​MenuItem"​)

The result looks something like this (we’re omitting some of the private internals that are just around for record keeping, and ordering the contents roughly by importance):

 %Absinthe.Type.Object{
 identifier:​ ​:menu_item​,
 name:​ ​"​​MenuItem"​,
 description:​ nil,
 fields:​ %{},
 interfaces:​ [],
 is_type_of:​ nil
 }

Absinthe models the types we define as Elixir structs. We haven’t added much to our menu_item type yet, so you can see the definition is rather sparse, but it’s still worth pointing out some of the main features. First, some internals:

identifier

The internal identifier Absinthe uses to refer to this type. As we define the schema, we’ll be using it a lot, too.

The rest of the Absinthe.Type.Object struct defines values that we can customize as needed:

description

Documentation we can provide for an object type that will automatically be available to API users using GraphQL’s built-in introspection features. We’ll be talking about this more in Running Our Query with GraphiQL.

name

The canonical GraphQL type name. While required, this will be generated for you automatically if you don’t provide it yourself, based on the Absinthe identifier. While we could customize this for our :menu_item object, the default of "MenuItem" works just fine.

fields

The real meat and potatoes of our object types. We’ll add some next.

is_type_of and interfaces

Support GraphQL’s Union and Interface abstract types, which we’ll talk more about in Understanding Abstract Types.

This is how Absinthe represents the types that you’ll be building. Now let’s move on to filling in your first type.

Adding Fields

Adding a field to an object type is as simple as using the field macro. The macro takes an identifier atom, a type reference, an optional keyword list of attributes, and a further optional block for more in-depth configuration. We’ll start with the basics and add :id, :name, and :description fields to our :menu_item object:

 object ​:menu_item​ ​do
  field ​:id​, ​:id
  field ​:name​, ​:string
  field ​:description​, ​:string
 end

The identifiers that we’ve chosen for the fields will give the fields canonical GraphQL names of "id", "name", and "description". (Like object types, the canonical GraphQL names of fields are generated for us automatically.)

The second argument to the field macro here defines the field type. These fields are simple scalar values, and the GraphQL specification defines a number of built-in scalar types you can use. We’ve including a handy, detailed reference in Appendix 1, GraphQL Types, but here are the basics:

GraphQLAbsintheDescription

Int

:integer

Signed 32-bit numeric non-fractional values

Float

:float

Signed double-precision fractional values as specified by IEEE 754[8]

String

:string

Textual data, represented as UTF-8 character sequences

Boolean

:boolean

true or false

Null

:null

Null values, represented as the keyword null

ID

:id

A unique identifier, often used to re-fetch an object or as the key for a cache; not intended to be human-readable

The built-in ID (:id) and String (:string) types are great fits for our :id and :name fields.

Custom Scalar Types

images/aside-icons/info.png

We can add our own scalar types too (we’ll learn more about that in Creating Your Own Scalar Types).

Let’s review our object by starting iex -S mix and then using Absinthe.Schema.lookup_ type/2 to see the fields we’ve added:

 iex(1)>​ Absinthe.Schema.lookup_type(PlateSlateWeb.Schema, ​"​​MenuItem"​)

This gives us something like this (again, shortened a bit for clarity):

 %Absinthe.Type.Object{
 identifier:​ ​:menu_item​,
 name:​ ​"​​MenuItem"​,
 description:​ nil,
 fields:​ %{
»id:​ %Absinthe.Type.Field{
 identifier:​ ​:id​,
 name:​ ​"​​id"​,
 type:​ ​:id​,
 args:​ %{},
 default_value:​ nil,
 deprecation:​ nil,
 description:​ nil,
 resolve:​ nil
  },
»name:​ %Absinthe.Type.Field{
 identifier:​ ​:name​,
 name:​ ​"​​name"​,
 type:​ ​:string​,
 args:​ %{},
 default_value:​ nil,
 deprecation:​ nil,
 description:​ nil,
 resolve:​ nil
  }
  },
 interfaces:​ [],
 is_type_of:​ nil
 }

There are additional options we can pass to the field macro to build out our Absinthe.Type.Field structs, but we’ll get into those soon enough. First let’s see about building a basic query.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.120.187