Module: ElasticGraph::Apollo::SchemaDefinition::ObjectAndInterfaceExtension

Included in:
InterfaceTypeExtension, ObjectTypeExtension
Defined in:
elasticgraph-apollo/lib/elastic_graph/apollo/schema_definition/object_and_interface_extension.rb

Overview

Instance Method Summary collapse

Instance Method Details

#apollo_entity_ref_field(name, type, id_field_name_in_index:, singular: nil) ⇒ void

Note:

This can be used for either a singleton or list reference, based on if type is a list.

Note:

Singleton references are automatically exposed on the generated GroupedBy type when their backing scalar ID is groupable. The existing scalar grouping remains available, and the grouped reference is always nullable. Filtering, sorting, aggregated values, and highlights are not enabled for entity references.

Note:

To opt out of automatic grouping for an individual reference, set f.groupable = false in the customization block. List references can opt into grouping on individual elements with singular:; paginated references do not support grouping.

This method returns an undefined value.

Exposes an Apollo entity reference as a new field, backed by an ID field.

When integrating an ElasticGraph project as a subgraph into a larger Apollo supergraph, it's useful to be able to reference entities owned by other subgraphs. The most straightforward way to do this is to define an entity reference type (e.g. a type containing just the @key fields such as id: ID and marked as resolvable: false in the @key directive), and then define fields using that type. This approach works particularly well when you plan ahead and know which ID fields to model with entity reference types.

However, on an existing schema where you've got some raw ID fields of external entities, it can be quite difficult to replace the ID fields with full-blown entity reference types, as doing so would require migrating clients and running a full backfill.

This API provides an alternate solution for this situation: it defines a GraphQL-only field which returns an entity reference type using a custom GraphQL resolver.

See the Apollo docs on referencing an entity without contributing fields for more information.

Examples:

Expose Review.product and Review.comments entity reference fields

ElasticGraph.define_schema do |schema|
  schema.object_type "Product" do |t|
    t.field "id", "ID"
    t.apollo_key fields: "id", resolvable: false
  end

  schema.object_type "Comment" do |t|
    t.field "id", "ID"
    t.apollo_key fields: "id", resolvable: false
  end

  schema.object_type "Review" do |t|
    t.field "id", "ID"
    t.field "score", "Int"

    # Fields originally defined in the first version of the schema
    t.field "productId", "ID"
    t.field "commentIds", "[ID!]!"

    # New field we're adding to expose the existing `productId` field as a `Product` entity reference.
    t.apollo_entity_ref_field "product", "Product", id_field_name_in_index: "productId"
    # ReviewGroupedBy automatically exposes both productId and product.

    # New field we're adding to expose the existing `commentIds` field as a list of `Comment` entity references.
    t.apollo_entity_ref_field "comments", "[Comment!]!", id_field_name_in_index: "commentIds"

    t.index "reviews"
  end
end

Parameters:

  • name (String) —

    Name of the field

  • type (String) —

    Name of the entity reference type (which must be defined separately)

  • id_field_name_in_index (String) —

    Name of the backing ID field in the datastore index

  • singular (String, nil) (defaults to: nil) —

    Name of the grouped field when opting into grouping on individual list elements

See Also:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'elasticgraph-apollo/lib/elastic_graph/apollo/schema_definition/object_and_interface_extension.rb', line 79

def apollo_entity_ref_field(name, type, id_field_name_in_index:, singular: nil)
  field(
    name,
    type,
    name_in_index: id_field_name_in_index,
    singular: singular,
    **LIMITED_GRAPHQL_ONLY_FIELD_OPTIONS.except(:groupable)
  ) do |f|
    validate_entity_ref_options(__method__.to_s, f, id_field_name_in_index, type) do |exposed_id_field|
      if f.type.list?
        f.resolve_with :apollo_entity_ref_list, source_ids_field: id_field_name_in_index, exposed_id_field: exposed_id_field
      else
        f.resolve_with :apollo_entity_ref, source_id_field: id_field_name_in_index, exposed_id_field: exposed_id_field
      end

      if f.groupable != false && groupable_by_id?(f, id_field_name_in_index)
        f.grouped_by_field_type = f.type.fully_unwrapped.name
        f.customize_grouped_by_field do |grouped_by_field|
          grouped_by_field.resolve_with :apollo_entity_grouped_by_ref, exposed_id_field: exposed_id_field
        end
      else
        f.groupable = false
      end
    end

    yield f if block_given?
  end
end

#apollo_entity_ref_paginated_collection_field(name, element_type, id_field_name_in_index:) ⇒ void

Note:

This requires id_field_name_in_index to be a list or paginated collection field.

Note:

The resulting field will only be available for clients to request as a return field. It will not support filtering, sorting, grouping, aggregated values, or highlights.

This method returns an undefined value.

Exposes a collection of Apollo entity references as a new paginated field, backed by an ID field.

When integrating an ElasticGraph project as a subgraph into a larger Apollo supergraph, it's useful to be able to reference entities owned by other subgraphs. The most straightforward way to do this is to define an entity reference type (e.g. a type containing just the @key fields such as id: ID and marked as resolvable: false in the @key directive), and then define fields using that type. This approach works particularly well when you plan ahead and know which ID fields to model with entity reference types.

However, on an existing schema where you've got some raw ID fields of external entities, it can be quite difficult to replace the ID fields with full-blown entity reference types, as doing so would require migrating clients and running a full backfill.

This API provides an alternate solution for this situation: it defines a GraphQL-only field which returns an entity reference type using a custom GraphQL resolver. In contrast to #apollo_entity_ref_field, this defines a field as a paginated Relay connection rather than a simple list.

See the Apollo docs on referencing an entity without contributing fields for more information.

Examples:

Expose Review.product and Review.comments entity reference fields

ElasticGraph.define_schema do |schema|
  schema.object_type "Comment" do |t|
    t.field "id", "ID"
    t.apollo_key fields: "id", resolvable: false
  end

  schema.object_type "Review" do |t|
    t.field "id", "ID"
    t.field "score", "Int"

    # Field originally defined in the first version of the schema
    t.field "commentIds", "[ID!]!"

    # New field we're adding to expose the existing `commentIds` field as a list of `Comment` entity references.
    t.apollo_entity_ref_paginated_collection_field "comments", "Comment", id_field_name_in_index: "commentIds"

    t.index "reviews"
  end
end

Parameters:

  • name (String) —

    Name of the field

  • element_type (String) —

    Name of the entity reference type (which must be defined separately)

  • id_field_name_in_index (String) —

    Name of the backing ID field in the datastore index

See Also:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'elasticgraph-apollo/lib/elastic_graph/apollo/schema_definition/object_and_interface_extension.rb', line 158

def apollo_entity_ref_paginated_collection_field(name, element_type, id_field_name_in_index:)
  paginated_collection_field(
    name,
    element_type,
    name_in_index: id_field_name_in_index,
    **LIMITED_GRAPHQL_ONLY_PAGINATED_FIELD_OPTIONS
  ) do |f|
    validate_entity_ref_options(__method__.to_s, f, id_field_name_in_index, element_type) do |exposed_id_field|
      backing_indexing_field = f.backing_indexing_field # : ::ElasticGraph::SchemaDefinition::SchemaElements::Field
      unless backing_indexing_field.type.list?
        raise Errors::SchemaError, "`#{f.parent_type.name}.#{f.name}` is invalid: `id_field_name_in_index` must reference an " \
          "id collection field, but the type of `#{id_field_name_in_index}` is `#{backing_indexing_field.type.name}`."
      end

      f.resolve_with :apollo_entity_ref_paginated, source_ids_field: id_field_name_in_index, exposed_id_field: exposed_id_field

      yield f if block_given?
    end
  end
end