relay: [Modern] Query only the local client extension types will cause compiling error

Let’s say I declare the client schema extensions

client.graphql

type Token {
  accessToken: String
  refreshToken: String
}

extend type Query {
  token: Token
}

And then in the QueryRenderer I made the query like this

query RootQuery {
  token {
    accessToken
    refreshToken
  }
}

The relay-compiler will fire the error

GraphQLCompilerContext: Unknown document `RootQuery`.

My temporary workaround is adding extra fields that can be fetched from remote.

query RootQuery {
  viewer {
    me {
      id
    }
  }
  token {
    accessToken
    refreshToken
  }
}

In this way, compiler can successfully compile the static query.

My assumption is relay-compiler will remove whatever client extensions from the query string, if I only query the local type, the query string will be stripped out entirely.

My question is “Is it possible for relay to query ONLY the local type?” People like me are excited about the client extensions is because it gives a way to get rid of the redux store. It will be really helpful if we can directly query the local type and use commitLocalUpdate for the state management.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 4
  • Comments: 35 (16 by maintainers)

Commits related to this issue

Most upvoted comments

try this as workaround

... on Query { __typename }

instead of just __typename

In the following example, using __typename because currently relay-compiler can’t query local extensions only. It can be any server field though but __typename fits because it’s domain agnostic and it’s just a String. By domain agnostic I mean you can use it no matter what your schema represents … it’s an introspection field.

const newUserFormQuery = graphql`
  query routes_NewUserQuery {
    __typename
    state: userForm {
      ...UserForm_state
    }
  }
`;

I can confirm the error and I’m also looking for the same functionality.

In my case is even a little bit more complex because my GraphQL API requires an accessToken and I have a UserConfirmationForm which I’d like to feed using a query to data in local schema. Currently, if I add server fields, then the compiler does not error but since the request lacks the accessToken when I render the component it receives a 401 response from server, without the data I expect from the local schema

@alunyov We should find a way to make local-only queries work somehow

Relay v4 https://github.com/facebook/relay/releases/tag/v4.0.0

it has full support to client schema extensions: full GraphQL types as well

Added full support for client schema extensions: now full GraphQL types can be defined in the client schema (as opposed to just extending existing server types), and they will be properly retained during garbage collection when rendered

can you test this again? or add a repro so we can investigate further?

@josephsavona yeah - there is a recent report of this too: https://github.com/facebook/relay/issues/3760

We’ll add this.

try this as workaround

... on Query { __typename }

instead of just __typename

@sibelius @josephsavona Hi

your suggestion works but we are getting a warning, can you give us any hints?

Screenshot 2021-01-25 at 11 58 21

@babangsund thanks for the tip! I am adding this to the list of changes that I want to submit PR’s for.

try useLocalQuery

https://gist.github.com/sibelius/43bd345873f84684c66942d9f06eeb2d

const useLocalQuery = <TQuery extends {response: any; variables: any}>(
  environment: Environment,
  query: any,
  inVariables: TQuery['variables'] = {}
): TQuery['response'] | null => {
  const variables = useDeepEqual(inVariables)
  const [dataRef, setData] = useRefState<SelectorData | null>(null)
  const disposablesRef = useRef<Disposable[]>([])
  useEffect(() => {
    const {getRequest, createOperationDescriptor} = environment.unstable_internal
    const request = getRequest(query)
    const operation = createOperationDescriptor(request, variables)
    const res = environment.lookup(operation.fragment, operation)
    setData(res.data || null)
    disposablesRef.current.push(environment.retain(operation.root))
    disposablesRef.current.push(
      environment.subscribe(res, (newSnapshot) => {
        setData(newSnapshot.data || null)
      })
    )
    const disposables = disposablesRef.current
    return () => {
      disposables.forEach((disposable) => disposable.dispose())
    }
  }, [environment, setData, query, variables])
  return dataRef.current
}

based on https://github.com/facebook/relay/issues/1656#issuecomment-515568094

I don’t know what the recommended way is for querying local-only fields, as the documentation is lacking in that part, but for now I have settled on this:

const getState = environment => {
  return environment.getStore().getSource().get('state');
};

It’s synchronous and avoids the “GraphQLCompilerContext: Unknown document” bug without having to use any workarounds.

@hisapy Thanks for the workaround. It works for me.

Hopefully in the future we don’t need the hack though.

@hisapy Can you elaborate on that by giving an example? (I’m not using this right now, so I’m only asking for the next reader that stumbles on this ticket.)