Best practices
There are some best practices we strongly believe in while developing with fuse
.
Use fragment co-location
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, readFragment } from '@/fuse'
export const UserFields = graphql(`
fragment Avatar_UserFields on User {
firstName
avatarUrl
}
`)
export const Avatar = (props: {
user: FragmentOf<typeof UserFields>
}) => {
const user = readFragment(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'
import { UserFields } from './Avatar'
const UserQuery = graphql(`
query User ($id: ID!) {
user(id: $id) {
...Avatar_UserFields
}
}
`, [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).
Don't interpolate arguments
When we are working with field-arguments
it's important to either write them as a literal value i.e.
query {
users(limit: 10) {
nodes {
id
name
}
}
}
or interpolate them as variables
query ($limit: Int!) {
users(limit: $limit) {
nodes {
id
name
}
}
}
String concatenation like limit: ${myJsLimitValue}
is discouraged!
Fragment naming
We suggest that you follow a naming pattern with fragments to make them both easy to discover as well as easy to understand to what component they relate.
When we name our fragments like Filename_TypeFields
we can easily see which component the fragment
relates to when it's being spread into a query. This allows
us to also easily know that we can remove the fragment once we remove the component.
An example of this could be the following, when we have an Author.ts
file we would define a fragment
that looks like
fragment Author_UserFields on User {
id
name
avatarUrl
}
Now when we spread it in our Blog
page we can easily see that it relates to the Author
component, when we
inspect the GraphQL document being sent to the server
we can also derive that this is meant for that component.
readFragment
and FragmentOf
You'll see that we use this readFragment
function in our components that have a fragment describing their
data-requirements, this helper ensures that we remove any extraneous fields and type the output correctly.
Another thing worth noting is how we type the props
, we use this FragmentOf
helper which ensures that
the parent-component uses the fragment and passes that into props, this means that if the parent-component
would define all the fields of the fragment manually and not spread it that we would show a warning.
Top-level queries
One of the benefits that comes with describing the data you need is that you perform 1 request and get 1 response, no waterfall where you need to wait for the list and then perform a whole set of other requests to enrich that data, ... We wrangle the data once, with this comes our sugestion to aggregate the data you need by means of your fragments at the page-level, in doing so you resolve all data in a single request.
With modals/... that pop up you can do one-off requests later when you need the data but not having a waterfall of loading spinners because you are navigating around feels both more performant and more user-friendly.