Craft GraphQL APIs in Elixir with Absinthe by Bruce Williams

Craft GraphQL APIs in Elixir with Absinthe by Bruce Williams

Author:Bruce Williams
Language: eng
Format: epub
Tags: Pragmatic Bookshelf
Publisher: Pragmatic Bookshelf


Setting Defaults

Now that we feel comfortable with def middleware, we’re in a good place to address an important question: how do fields without specific resolvers actually resolve anything? Throughout the entire book so far, we’ve had stuff in our schema like this:

​ object ​:menu_item​ ​do​

​ field ​:name​, ​:string​

​ ​# ... other fields​

​ ​end​

Despite the fact that the :name field has no obvious resolver, we nonetheless can include it in a GraphQL query and we get a result. What we need to do is look at what actually happens here because what you’ll find is not only a feature you can use yourself, but more importantly, a tool you can customize as necessary when the default behavior doesn’t suit the data you are working with.

As you might remember from ​Making a Query​, what happens at this point is that the default resolution logic does something equivalent to this:

​ field ​:name​, ​:string​ ​do​

​ resolve ​fn​ parent, _, _ ->

​ {​:ok​, Map.get(parent, ​:name​)}

​ ​end​

​ ​end​

Any time a def middleware callback returns an empty list of middleware for a field, Absinthe adds the incredibly simple middleware spec [{Absinthe.Middleware.MapGet, field.identifier}]. Here it is in full:

​ ​def​ call(%{​source:​ source} = resolution, key) ​do​

​ %{resolution | ​state:​ ​:resolved​, ​value:​ Map.get(source, key)}

​ ​end​

This is handy when the parent entity in question is a map with atom keys, but it isn’t what we want in every scenario.

It’s increasingly common that an API will expose data from a variety of data sources, only some of which may have a fully structured schema on hand that will give you nice maps with atom keys. Whether you’re hitting a NoSQL database or calling out to a third-party API for JSON data, you’re going to eventually run into a situation where the data that you want to expose via GraphQL has string keys or keys that aren’t quite what you want.

We can get some of this NoSQL experience without even changing databases, as PostgreSQL has significantly expanded its NoSQL features, and now offers a JSONB column type with which we can store a JSON blob. We’re going to add a column to our items table that uses this JSONB type and in it we’ll store allergy information about the menu items.

Start by creating the database migration:

​ $ mix ecto.gen.migration add_allergy_info_to_menu_item

Add the column in the migration file:

07-chp.middleware/5-default/priv/repo/migrations/20170828023859_add_allergy_info_to_menu_item.exs

​ ​defmodule​ PlateSlate.Repo.Migrations.AddAllergyInfoToMenuItem ​do​

​ ​use​ Ecto.Migration

​ ​def​ change ​do​

​ alter table(​:items​) ​do​

​ add ​:allergy_info​, ​:map​

​ ​end​

​ ​end​

​ ​end​

Then expose it in Elixir by adding the field in the schema module:

07-chp.middleware/5-default/lib/plate_slate/menu/item.ex

​ field ​:allergy_info​, {​:array​, ​:map​}

That’s it as far as the underlying data schema is concerned. Let’s run the database migration, and then add a menu item with allergy information:

​ ​$ ​​mix​​ ​​ecto.migrate​

In the book code for this chapter, we’ve added a new item, “Thai Salad,” that contains some allergy information. If you don’t want to reset your database, you can just copy and paste this into an iex -S mix session:

07-chp.middleware/5-default/dev/support/seeds.ex

​ alias PlateSlate.{Menu, Repo}

​ category = Repo.get_by(Menu.Category, ​name:​ ​"​​Sides"​)

​ %Menu.Item{

​ ​name:​ ​"​​Thai Salad"​,

​ ​price:​ 3.50,

​ ​category:​ category,

​ ​allergy_info:​ [



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.