Defining a Root Mutation Type

To support mutation operations, we need to define a root mutation type, just as we did for queries. This will be used as the entry point for GraphQL mutation operations, and it will define—based on the mutation fields that we add—the complete list of capabilities users of our API will have available to modify data.

We define the root mutation type by using the mutation macro in our schema:

 mutation ​do
 # Mutation fields will go here
 end

This might strike you as somewhat similar to our use of the query macro when we did the initial build-out of the PlateSlate Absinthe schema, and that’s by design. Both macros—query and mutation—are used to define the root object types for their respective GraphQL operations.

A mutation root type won’t do us any good unless we define mutation fields. Let’s do that next, adding a mutation for menu item creation.

Adding a Mutation Field

Now that we have a place to put our mutations, let’s get organized and come up with a plan for menu item creation. Here’s the narrative of how we expect this feature to work:

  • A user will build out the menu item details in their user interface (website, mobile application, anything that can talk GraphQL).

  • The user’s client will send the collected data for a menu item to our GraphQL API, indicating the createMenuItem mutation and the data they’d like in response.

  • Our API will execute the mutation, create the menu item (if possible), and return the requested data.

Let’s build out the mutation field that clients need. Just as we would with query fields and the query root mutation type, we add mutation fields directly inside the mutation block (which defines the root mutation object type). Here, we add a :create_menu_item field:

 mutation ​do
 
  field ​:create_menu_item​, ​:menu_item​ ​do
 end
 
 end

Most mutations that create something return it as the result of the field, which is why the mutation here is returning a :menu_item. We’ll talk more about why this is handy soon, but first let’s get into the nitty gritty of receiving the input to create the menu item.

Modeling with an Input Object

To create menu items, you’ll need to accept information from the client. You’ll use an input object to model the data that you’re expecting. Let’s keep it simple for now, just accepting a name, optional description, price, and category ID in a new input object type, :menu_item_input:

 input_object ​:menu_item_input​ ​do
  field ​:name​, non_null(​:string​)
  field ​:description​, ​:string
  field ​:price​, non_null(​:decimal​)
  field ​:category_id​, non_null(​:id​)
 end

These fields represent the subset of fields that are needed to support clients creating menu items. We can expand this as the menu management features that we need grow with our application.

Object Types Aren’t Input Types

images/aside-icons/warning.png

It’s easy to forget that you can’t use object types for user input; instead, you need to create input object types for use in arguments. While this might seem like unnecessary work at first, you’ll come to appreciate the way it forces you to focus on the discrete package of data that you need for specific mutations.

There are also some technical differences between objects and input objects. Input object fields can only be valid input types, which excludes unions, interfaces, and objects. You also can’t form cycles with input objects, whereas cycles are permitted with objects.

Our GraphQL schema doesn’t know what the :decimal type is yet, so we need to define that as well. Absinthe ships with a :float scalar type—a built-in type to meet the requirements of the GraphQL specification—but a float is a poor choice for monetary math operations. Thankfully, we’ve already got the decimal package in our mix.exs file, which will let us properly represent our menu item prices.

Let’s define the :decimal type using the scalar macro, just as we did in Creating Your Own Scalar Types:

 scalar ​:decimal​ ​do
  parse ​fn
  %{​value:​ value}, _ ->
  Decimal.parse(value)
  _, _ ->
 :error
 end
  serialize &to_string/1
 end

Now that we have a decimal type, we can expose a :price field on the :menu_item object type that we defined all the way back in Defining an Object Type, too:

 object ​:menu_item​ ​do
 
  interfaces [​:search_result​]
 
  field ​:id​, ​:id
  field ​:name​, ​:string
  field ​:description​, ​:string
» field ​:price​, ​:decimal
  field ​:added_on​, ​:date
 end

Let’s define an :input argument on our :create_menu_item field, using our :menu_ item_input type:

 mutation ​do
 
  field ​:create_menu_item​, ​:menu_item​ ​do
  arg ​:input​, non_null(​:menu_item_input​)
  resolve &Resolvers.Menu.create_item/3
 end
 
 end

We are using the name input here because it’s a convention of the Relay client-side framework[19] for mutations, but we could use a different name instead if we were so inclined. (We’re not, for now.) We’ve also wired in the resolver function, located with the other menu-related resolvers in Plate- SlateWeb.Schema.Resolvers.Menu.

The actual behavior that will occur when users use createMenuItem in GraphQL documents is the responsibility of the resolver function for our mutation field. We need to make the resolver interpret our client input, attempt to persist the menu item, and respond to users.

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

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