Documentation
Basics
Querying from the client
Overview

Querying from the client

Depending on whether you are querying data from React Server Components, a client component in the app router, or a client component in the pages router, you will need to use a different set of data fetching helpers.

Fragments

One of the pillars of fuse is Fragment co-location, we strongly believe that this patterns helps us get a better overview of our data-requirements and makes it easier to reason about the data a component will receive.

When creating components it's useful to co-locate your data-requirements with your component, this way when you need more data for your component you know exactly where to add it and you don't have to go up your component-tree to find the query responsible for adding the data.

import { FragmentType, graphql, useFragment } from '@/fuse'
 
const UserFields = graphql(`
  fragment Avatar_UserFields on User {
    firstName
    avatarUrl
  }
`)
 
export const Avatar = (props: {
  user: FragmentType<typeof UserFields>
}) => {
  const user = useFragment(UserFields, props.user)
 
  return (
    <div>
      {(user.avatarUrl && user.firstName) && <img src={user.avatarUrl} alt={user.firstName} />}
      <span>Welcome, {user.firstName}</span>
    </div>
  )
}

The above defined fragment is now globally available and we can add it to our query:

import { graphql } from '@/fuse'
 
const UserQuery = graphql(`
  query User ($id: ID!) {
    user(id: $id) {
      ...Avatar_UserFields
    }
  }
`)

From now on out, every time we need a new field in the Avatar component we can just add it there and trust that the query is updated automatically and our data is passed into the component by menans of <Avatar user={result.data.user} />.

All Fragments you define in a component will be globally available, this means that if your client components define their data requirements you can spread your fragments in your top-level server-component and pass the data down.

Type-conditions

Something you might notice when we define a fragment is that we add this on X keyword after the name, this is called a type-condition and is used to tell the GraphQL that we only want those fields when the type is either X or a type that inherits from X (like with interfaces).

Generated files

When you run next dev for the first time you'll see the fuse/ folder populate with a bunch of files. These don't need to be touched and are mostly there to help with typing your results, variables and facilitating the client-methods.

Let's go over each file:

  • fuse/server.ts: This is the main entry-point for the /app directory when you are working with server-components and server-actions. It exports an execute function which takes a Document and variables and will perform that on the datalayer.
  • fuse/client.ts: This is the main entry-point for the /app directory when you are in 'use client' mode. It exports the useQuery/... hooks which you can use in your components to talk to your datalayer.
  • fuse/pages.ts: This is the main entry-point for the /pages directory. It exports the withUrqlClinet and initUrqlClient to work with getServerSideProps, ... As well as React hooks which you can use in your components.
  • fuse/fragment-masking.ts: The useFragment and FragmentType helpers are exported from here, these helpers basically ensure that the props you pass in will be reduced to the selection of fields of your fragment.
  • fuse/gql.ts: The graphql() function is exported from here, this helper ensures that both the Result and Variables are typed correctly for a given Document.
  • fuse/graphql.ts: This file contains a translation of your GraphQL schema to TypeScript types. It also contains the Document type which is a helper type to type your GraphQL documents.