Skip to content

Would it be possible to use Fragments just for pure re-usability? (without forced parent-child component encapsulation) #76

Description

@rellampec

In regards to the ImplicitlyFetchedFieldError, that is raised in GraphQL::Client::Schema::ObjectType, on method_missing...

              if @data.key?(field.name)
                raise ImplicitlyFetchedFieldError

Would it be possible to have the option to use fragments just for re-usability?

For example, in the Apollo documentation on fragments, it states that

Image

the main reason for fragments to exist is re-usability. It certainly emphasizes that it is specially useful when collocating with components, but it does NOT say that this is the only usage that we should be able to make out of it.

In the documentation (see collocated fragments), somehow states that there are two types of fragments, the ones that are "externally" (or collaterally) referred to by a component, and the ones that are defined right on that component (collated):

A colocated fragment is just like any other fragment, except it's defined in the same file as a particular component that uses the fragment's fields.

After you define a fragment in a child component, the parent component can refer to it in its own colocated fragments, like so:

export const FEED_ENTRY_FRAGMENT = gql`
  fragment FeedEntryFragment on FeedEntry {
    commentCount
    repository {
      full_name
      html_url
      owner {
        avatar_url
      }
    }
    ...VoteButtonsFragment
    ...EntryInfoFragment
  }

  ${VOTE_BUTTONS_FRAGMENT}
  ${ENTRY_INFO_FRAGMENT}
  • In this example VOTE_BUTTONS_FRAGMENT and ENTRY_INFO_FRAGMENT are defined in (or imported from) child components.
Image

Apollo's technique to define fragments and identify if they belong to a parent or children classes can arguable, yet they offer a way.


Following the example of graphlient gem, where fragments support was added by making it aligned with graphql-client, in the following query...

module Fragments
  Invoice = client.parse <<~'GRAPHQL'
    fragment on Invoice {
      id
      feeInCents
    }
  GRAPHQL
end

invoice_query = client.parse do
  query do
    invoice(id: 10) do
      id
      ___Fragments__Invoice
    end
  end
end

... we would get the error if trying to access feeInCents directly from the query Response

response = client.execute(invoice_query)
result = response.data.invoice
result.to_h
# {"id" => 10, "feeInCents"=> 20000}
result.id
# 10
result.fee_in_cents
# raises GraphQL::Client::ImplicitlyFetchedFieldError

How would we in graphql-client define a fragment that belongs to the main query (or parent component)?

  invoice_query <<~'GRAPHQL'
    query InvoiceById($some_id: Int) {
      invoice(id: $some_id) {
        id
        ...withFee
      }
    }

    fragment withFee on Invoice {
      id
      feeInCents
    }
  GRAPHQL

provided that we can do something like this:

response = client.execute(invoice_query)
result = response.data.invoice
result.fee_in_cents

without getting the error GraphQL::Client::ImplicitlyFetchedFieldError.

Activity

  1. changed the title [-]Would it be possible to use Fragments without parent-child component encapsulation?[/-] [+]Would it be possible to use Fragments just for pure re-usability? (without forced parent-child component encapsulation)[/+] on Jul 9, 2025
  2. rellampec commented on Aug 5, 2025

    @rellampec
    Author

    I guess this library isn't maintained any longer ??

  3. rellampec commented on Jun 15, 2026

    @rellampec
    Author

    Picking this up — short answer: yes, this is now supported, and it works exactly the way the original question hoped (Apollo-style).

    How to do it today

    Define a standard named GraphQL fragment inline, in the same parse string as the operation that spreads it, and its fields are directly accessible on the result — no ImplicitlyFetchedFieldError, no re-wrapping through a fragment constant:

    Queries = Client.parse <<-'GRAPHQL'
      fragment UserDetails on User {
        fullName
        location
      }
    
      query UserQuery {
        user(name: "Josh") {
          ...UserDetails
        }
      }
    GRAPHQL
    
    result = Client.query(Queries::UserQuery)
    result.data.user.full_name  # works — inline fragment, no isolation enforced
    result.data.user.location   # works

    This mirrors Apollo Client's colocating fragments convention: a fragment used purely for reuse, defined next to its operation, fields accessible — without forcing a parent–child component hierarchy. The constant-bound style (fragment on Type { … } assigned to a Ruby constant) is still there for when you do want ImplicitlyFetchedFieldError to guard cross-module access. The two are complementary, and you pick per fragment.

    What I've opened

    • docs: document inline (colocated) named fragments #80 — documents the inline / colocated style in the README and the ImplicitlyFetchedFieldError guide. This behaviour already existed in Client#parse (the local definition is kept and the fields flow through
      source_document) — it just wasn't documented anywhere.
    • test: characterize Client#parse handling of inline named fragments #81 — characterization tests that pin the current Client#parse fragment-resolution behaviour, so the fix below can't change it silently.
    • fix: anchor inline fragment-definition detection in Client#parse #82 — hardens the parser. The spread/definition disambiguation used an unanchored match
      (/fragment\s*#{const_name}/), which matched too eagerly: a ...UserFields spread could be mistaken for a fragment UserFieldsExtended on User definition and left unresolved (which actually crashed parsing in sliced_definitions).
      It's now anchored to fragment <Name> on, with tests for the prefix-collision case and for a constant fragment coexisting with a prefix-sharing inline fragment (the realistic migration case).

    Status

    I'm leaving this issue open until #82 merges and ships — at that point the "fragments for pure reuse,
    Apollo-style" request here is fully and robustly resolved, and I'll close it referencing the release.

  4. rellampec commented on Jun 23, 2026

    @rellampec
    Author

    @rmosolgo I have had a look at your roadmap to make sure this gem goes up. Could you please have a look at what has been done here, and give me your 5 cents on whether that's the path that we can take to make this gem scalable? (i.e. conventional re-using fragments via inline VS parent-child encapsulation via constants; backwards compatibility and convention-over-configuration principle + aligned with an industry reference: Apollo).

    There are several MRs I have raised, each one with its own piece, so it gets easier to review and merge.

    I know this may seem playing on the edge, at the current status and pace, but it is really going to be a booster. There are far more MRs I would like to contribute with to this particular gem. But first, there's need to remove some functional blockers.

  5. rellampec commented on Jun 29, 2026

    @rellampec
    Author

    @josh @jhawthorn @jmeridth ... could you please have a look at these 4 MRs and allow workflow checks? You can be selective and reject any MR if you see doesn't fit. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions