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

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

Overview

Extends SchemaDefinition::SchemaElements::ObjectType and SchemaDefinition::SchemaElements::InterfaceType to offer some Apollo-specific APIs.

Instance Method Summary collapse

Instance Method Details

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

Note:

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

Note:

The resulting field will be 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 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](www.apollographql.com/docs/graphos/schema-design/federated-schemas/entities/contribute-fields#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"

    # 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

See Also:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/elastic_graph/apollo/schema_definition/object_and_interface_extension.rb', line 74

def apollo_entity_ref_field(name, type, id_field_name_in_index:)
  field(
    name,
    type,
    name_in_index: id_field_name_in_index,
    **LIMITED_GRAPHQL_ONLY_FIELD_OPTIONS
  ) 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
    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 be 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](relay.dev/graphql/connections.htm) rather than a simple list.

See the [Apollo docs on referencing an entity without contributing fields](www.apollographql.com/docs/graphos/schema-design/federated-schemas/entities/contribute-fields#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:



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/elastic_graph/apollo/schema_definition/object_and_interface_extension.rb', line 143

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