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 anexecute
function which takes aDocument
andvariables
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 theuseQuery
/... 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 thewithUrqlClinet
andinitUrqlClient
to work withgetServerSideProps
, ... As well as React hooks which you can use in your components.fuse/fragment-masking.ts
: TheuseFragment
andFragmentType
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
: Thegraphql()
function is exported from here, this helper ensures that both theResult
andVariables
are typed correctly for a givenDocument
.fuse/graphql.ts
: This file contains a translation of your GraphQL schema to TypeScript types. It also contains theDocument
type which is a helper type to type your GraphQL documents.