absinthe_relay v1.4.5 Absinthe.Relay.Node.ParseIDs View Source

Parse node (global) ID arguments before they are passed to a resolver, checking the arguments against acceptable types.

For each argument:

  • If a single node type is provided, the node ID in the argument map will be replaced by the ID specific to your application.
  • If multiple node types are provided (as a list), the node ID in the argument map will be replaced by a map with the node ID specific to your application as :id and the parsed node type as :type.

If a GraphQL null value for an ID is found, it will be passed through as nil in either case, since no type can be associated with the value.

Examples

Parse a node (global) ID argument :item_id as an :item type. This replaces the node ID in the argument map (key :item_id) with your application-specific ID. For example, "123".

field :item, :item do
  arg :item_id, non_null(:id)

  middleware Absinthe.Relay.Node.ParseIDs, item_id: :item
  resolve &item_resolver/3
end

Parse a node (global) ID argument :interface_id into one of multiple node types. This replaces the node ID in the argument map (key :interface_id) with map of the parsed node type and your application-specific ID. For example, %{type: :thing, id: "123"}.

field :foo, :foo do
  arg :interface_id, non_null(:id)

  middleware Absinthe.Relay.Node.ParseIDs, interface_id: [:item, :thing]
  resolve &foo_resolver/3
end

Parse a nested structure of node (global) IDs. This behaves similarly to the examples above, but acts recursively when given a keyword list.

input_object :parent_input do
  field :id, non_null(:id)
  field :children, list_of(:child_input)
  field :child, non_null(:child_input)
end

input_object :child_input do
  field :id, non_null(:id)
end

mutation do
  payload field :update_parent do
    input do
      field :parent, :parent_input
    end

    output do
      field :parent, :parent
    end

    middleware Absinthe.Relay.Node.ParseIDs, parent: [
      id: :parent,
      children: [id: :child],
      child: [id: :child]
    ]
    resolve &resolve_parent/2
  end
end

As with any piece of middleware, this can configured schema-wide using the middleware/3 function in your schema. In this example all top level query fields are made to support node IDs with the associated criteria in @node_id_rules:

defmodule MyApp.Schema do

  # Schema ...

  @node_id_rules [
    item_id: :item,
    interface_id: [:item, :thing],
  ]
  def middleware(middleware, _, %Absinthe.Type.Object{identifier: :query}) do
    [{Absinthe.Relay.Node.ParseIDs, @node_id_rules} | middleware]
  end
  def middleware(middleware, _, _) do
    middleware
  end

end

Using with Mutations

Important: Remember that middleware is applied in order. If you’re using middleware/3 to apply this middleware to a mutation field (defined using the Absinthe.Relay.Mutation macros) before the Absinthe.Relay.Mutation middleware, you need to include a wrapping top-level :input, since the argument won’t be stripped out yet.

So, this configuration defined inside of a payload field block:

mutation do

  payload field :change_something do

    # ...
    middleware Absinthe.Relay.Node.ParseIDs, profile: [
      user_id: :user
   ]

  end

end

Needs to look like this if you put the ParseIDs middleware first:

def middleware(middleware, %Absinthe.Type.Field{identifier: :change_something}, _) do
  # Note the addition of the `input` level:
  [{Absinthe.Relay.Node.ParseIDs, input: [profile: [user_id: :user]]} | middleware]
end
def middleware(middleware, _, _) do
  middleware
end

If, however, you do a bit more advanced surgery to the middleware list and insert Absinthe.Relay.Node.ParseIDs after Absinthe.Relay.Mutation, you don’t include the wrapping :input.

Compatibility Note for Middleware Developers

If you’re defining a piece of middleware that modifies field arguments similar to Absinthe.Relay.Mutation does (stripping the outer input argument), you need to set the private :__parse_ids_root so that this middleware can find the root schema node used to apply its configuration. See Absinthe.Relay.Mutation for an example of setting the value, and the find_schema_root!/2 function in this module for how it’s used.

Link to this section Summary

Types

The rules used to parse node ID arguments

Link to this section Types

Link to this type full_result() View Source
full_result() :: %{type: atom(), id: simple_result()}
Link to this type rules() View Source
rules() ::
  [{atom(), atom() | [atom()]}] | %{optional(atom()) => atom() | [atom()]}

The rules used to parse node ID arguments.

Examples

Declare :item_id as only valid with the :item node type:

[
  item_id: :item
]

Declare :item_id be valid as either :foo or :bar types:

[
  item_id: [:foo, :bar]
]

Note that using these two different forms will result in different argument values being passed for :item_id (the former, as a binary, the latter as a map).

In the event that the ID is a null, it will be passed-through as nil.

See the module documentation for more details.

Link to this type simple_result() View Source
simple_result() :: nil | binary()