Input Types

View Source

Input types define the structure of complex arguments, typically used in mutations. Unlike object types, input types can only contain scalar fields and other input types.

Basic Usage

defmodule MyApp.GraphQL.Inputs.CreateUserInput do
  use GreenFairy.Input

  input "CreateUserInput" do
    field :email, non_null(:string)
    field :name, :string
    field :password, non_null(:string)
  end
end

This generates:

input CreateUserInput {
  email: String!
  name: String
  password: String!
}

Using Input Types

Reference inputs in mutations or queries:

defmodule MyApp.GraphQL.Mutations.UserMutations do
  use GreenFairy.Mutation

  mutations do
    field :create_user, :user do
      arg :input, non_null(:create_user_input)

      resolve fn _, %{input: input}, ctx ->
        MyApp.Accounts.create_user(input, ctx)
      end
    end
  end
end

GraphQL query:

mutation {
  createUser(input: {
    email: "user@example.com"
    name: "John Doe"
    password: "secret123"
  }) {
    id
    email
  }
}

Field Options

Default Values

input "CreatePostInput" do
  field :title, non_null(:string)
  field :body, :string
  field :status, :post_status, default_value: :draft
  field :published, :boolean, default_value: false
  field :tags, list_of(:string), default_value: []
end

Descriptions

input "CreateUserInput" do
  @desc "User's email address (must be unique)"
  field :email, non_null(:string)

  @desc "Display name shown to other users"
  field :name, :string

  field :role, :user_role, description: "Initial role assignment"
end

Nested Input Types

Input types can reference other input types:

defmodule MyApp.GraphQL.Inputs.AddressInput do
  use GreenFairy.Input

  input "AddressInput" do
    field :street, non_null(:string)
    field :city, non_null(:string)
    field :state, :string
    field :postal_code, non_null(:string)
    field :country, non_null(:string), default_value: "US"
  end
end

defmodule MyApp.GraphQL.Inputs.CreateOrganizationInput do
  use GreenFairy.Input

  input "CreateOrganizationInput" do
    field :name, non_null(:string)
    field :billing_address, non_null(:address_input)
    field :shipping_address, :address_input
  end
end

Input Authorization

Control which fields users can submit:

input "UpdateUserInput" do
  authorize fn _input, ctx ->
    if ctx[:current_user]?.admin, do: :all, else: [:name, :email]
  end

  field :name, :string
  field :email, :string
  field :role, :user_role  # Admin only
end

See the Authorization Guide for details.

Common Patterns

Create vs Update Inputs

Separate inputs for create (required fields) and update (optional fields):

defmodule MyApp.GraphQL.Inputs.CreateUserInput do
  use GreenFairy.Input

  input "CreateUserInput" do
    field :email, non_null(:string)
    field :password, non_null(:string)
    field :name, :string
  end
end

defmodule MyApp.GraphQL.Inputs.UpdateUserInput do
  use GreenFairy.Input

  input "UpdateUserInput" do
    # All fields optional for partial updates
    field :email, :string
    field :name, :string
    field :avatar_url, :string
  end
end

Relay Mutation Inputs

For Relay-compliant mutations, include clientMutationId:

defmodule MyApp.GraphQL.Inputs.CreatePostInput do
  use GreenFairy.Input

  input "CreatePostInput" do
    field :client_mutation_id, :string
    field :title, non_null(:string)
    field :body, :string
    field :author_id, non_null(:id)
  end
end

Or use the relay_mutation macro which handles this automatically. See the Relay Guide.

Filter Inputs

For query filtering (separate from CQL auto-generated filters):

defmodule MyApp.GraphQL.Inputs.UserFilterInput do
  use GreenFairy.Input

  input "UserFilterInput" do
    field :search, :string
    field :role, :user_role
    field :status, :user_status
    field :created_after, :datetime
    field :created_before, :datetime
    field :has_verified_email, :boolean
  end
end

Batch Operation Inputs

defmodule MyApp.GraphQL.Inputs.BulkUpdateInput do
  use GreenFairy.Input

  input "BulkUserUpdateInput" do
    field :ids, non_null(list_of(non_null(:id)))
    field :status, :user_status
    field :role, :user_role
  end
end

Validation

Input validation is typically done in your resolver or context module:

field :create_user, :user do
  arg :input, non_null(:create_user_input)

  resolve fn _, %{input: input}, _ctx ->
    # Use Ecto changesets for validation
    case MyApp.Accounts.create_user(input) do
      {:ok, user} ->
        {:ok, user}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:error, format_changeset_errors(changeset)}
    end
  end
end

defp format_changeset_errors(changeset) do
  Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
    Enum.reduce(opts, msg, fn {key, value}, acc ->
      String.replace(acc, "%{#{key}}", to_string(value))
    end)
  end)
end

Complete Example

defmodule MyApp.GraphQL.Inputs.CreateOrderInput do
  use GreenFairy.Input

  input "CreateOrderInput" do
    @desc "Input for creating a new order"

    authorize fn _input, ctx ->
      if ctx[:current_user] do
        :all
      else
        :none
      end
    end

    @desc "Customer placing the order"
    field :customer_id, non_null(:id)

    @desc "Items to include in the order"
    field :items, non_null(list_of(non_null(:order_item_input)))

    @desc "Shipping address"
    field :shipping_address, non_null(:address_input)

    @desc "Billing address (defaults to shipping if not provided)"
    field :billing_address, :address_input

    @desc "Discount code to apply"
    field :discount_code, :string

    @desc "Special instructions for delivery"
    field :notes, :string

    @desc "Requested delivery date"
    field :requested_delivery_date, :date
  end
end

defmodule MyApp.GraphQL.Inputs.OrderItemInput do
  use GreenFairy.Input

  input "OrderItemInput" do
    field :product_id, non_null(:id)
    field :quantity, non_null(:integer), default_value: 1
    field :customizations, :json
  end
end

Module Functions

Every input module exports:

FunctionDescription
__green_fairy_kind__/0Returns :input
__green_fairy_identifier__/0Returns the type identifier
__green_fairy_definition__/0Returns the full definition map
__filter_input__/2Filters input based on authorization
__filter_input__/3Filters with options (e.g., strict: true)

Naming Conventions

GraphQL NameElixir IdentifierModule Suggestion
CreateUserInput:create_user_inputMyApp.GraphQL.Inputs.CreateUserInput
UpdatePostInput:update_post_inputMyApp.GraphQL.Inputs.UpdatePostInput
AddressInput:address_inputMyApp.GraphQL.Inputs.AddressInput

Input vs Object Types

FeatureInput TypesObject Types
Used forArgumentsQuery results
Can have resolversNoYes
Can reference objectsNoYes
Can reference inputsYesNo (use args)
Can have connectionsNoYes
Can implement interfacesNoYes

Next Steps