Colocated Fragments:  Organizing your GraphQL queries in React
Tags: react, graphql, typescript, javascript
Published: 2020-10-18 00:39:00

## ¿Who is this article for?

Developers working on a [React](https://reactjs.org/) project, that consumes data from a [GraphQL](https://graphql.org/) API and wants to find a new way to organize their queries definitions.

## Introduction

There are multiple ways for organizing your queries, but normally, you'll find a variation of these two methods:

- Saving all your queries in a single file. ie: [queries.ts](https://github.com/tenhsor1/tutorial-graphql-queries-examples/blob/single-file/src/queries.ts).
- Colocating your full query definition next to the component consuming it. [Example](https://github.com/tenhsor1/tutorial-graphql-queries-examples/blob/no-fragments/src/pages/Post.tsx#L9).

In this article, we are going to focus on learning a variation based on the second method, where we colocate our queries next to the parent components that execute them, and with Fragments, we colocate the consumed fields next to the child components consuming them.

## ¿What is a Fragment?

A Fragment can be defined as a reusable unit of information.

From [GraphQL docs](https://graphql.org/learn/queries/#fragments):

> Fragments let you construct sets of fields, and then include them in queries where you need to.

### Why Fragments are useful?

Let's use a Blog project as an example. Let's suppose we have a GraphQL `post` query, which returns a post's content, it author's information, and each of the post's comments:

```graphql
// Without Fragment
post(id: ID!) {
  id
  title
  content
  date
  author {
    id
    name
    image
    email
  }
  comments {
    id
    content
    date
    author {
      id
      name
      image
      email
    }
  }
}
```

You can see that we are asking for the author's information twice (`id`, `name`, `image`, `email`), one for the blog's author, and the other one for the authors of the comments. Now, let's take a look at this same example, but now using Fragments:

```graphql
// With Fragment
post(id: ID!) {
  id
  title
  content
  date
  author {
    ...Avatar
  }
  comments {
    id
    content
    date
    author {
      ...Avatar
    }
  }
}

fragment Avatar on User {
  id
  name
  image
  email
}
```

As you can see, we are naming our Fragment `Avatar` and we are indicating that it can only be used by `User` types. The way to consume Fragments is through the spread operator followed by the Fragment name: `...Avatar`. All the fields from the Fragment will be included in the section/field where is being called.

As you can see, we are naming our Fragment `Avatar` and we are indicating that it can only be used by `User` types. The way to consume Fragments is through the spread operator followed by the Fragment name: `...Avatar`. All it fields will be included in the section/field where this is being called.

Fragments are useful, but when you combine them with React components, they become powerful.

## Colocated Fragments

From GraphQL client [Apollo docs](https://www.apollographql.com/docs/react/data/fragments/#creating-colocated-fragments):

> A colocated fragment is just like any other fragment, except it is attached to a particular component that uses the fragment's fields.

Basically, is "colocate" the Fragment definition next to the component that is gonna consume its information.

### Creating a Colocated Fragment

Let's use an `Avatar` component as an example. This component will render a user's information. 

This is how it would look like with a Colocated Fragment:

```jsx
// Avatar.jsx
import gql from 'graphql-tag';

export const Avatar = ({ user }) => {
  return (
    <div>
      <a href={`/user/${user.id}`}>
        <h3>{user.name}</h3>
        <img src={user.image} />
      </a>
    </div>
  );
};

Avatar.fragments = {
  user: gql`
    fragment Avatar on User {
      id
      name
      image
    }
  `
};
```

There are three important things happening here:

- First we defined a new Fragment called `Avatar`. There are no  explicit rules on how to name Fragments, but to avoid name collisions, A good alternative is to name them the same as the component they are attached to.
- We export the Colocated Fragment by creating a new `fragments` attribute in the `Avatar`Component.
    - [Apollo](https://www.apollographql.com/docs/react/data/fragments/#creating-colocated-fragments) proposes to export Colocated Fragments using this attribute, however, this is a matter of preference, just make sure that you set a convention. (If you use typescript, you can create a new component type to force the inclusion of the `fragments` attribute).
- Finally, this component consumes the data through a `user` prop, which includes the same fields as the Fragment: `id`, `image` and `name`. (If you use typescript, There's a "step by step" section on how to generate your prop types automatically based on the Colocated Fragment definition).

### Consuming a Colocated Fragment

You can only realize Colocated Fragments magic when you start consuming them. Let's use a `PostHeader` component as an example, which will use the `Avatar` component for rendering the author information:

```jsx
// PostHeader.jsx
import gql from 'graphql-tag';
import { Avatar } from './Avatar';
export const PostHeader = ({ post }) => {
  return (
    <div>
      <Avatar user={post.author} />
      <Link to={`/post/${post.id}`}>
        <h1>{post.title}</h1>
      </Link>
    </div>
  );
};

PostHeader.fragments = {
  post: gql`
    fragment PostHeader on Post {
      id
      title
      author {
        ...Avatar
      }
    }
    ${Avatar.fragments.user}
  `
};
```

Let's analyze what's happening:

- First, we import the `Avatar` component, which is used by `PostHeader` for rendering the author's information, but when we import  `Avatar` we are also importing it Colocated Fragments through the `fragment` attribute!
- At the same time, we are creating a `PostHeader` Colocated Fragment, which is composed of some individual fields and the `author` field. This field uses `...Avatar` Colocated Fragment for importing its fields. Here we can see that React composition magic is now available for our GraphQL queries!
- We make the `Avatar`Colocated Fragment accessible through javascript string interpolation: `${Avatar.fragments.user}`.
- Finally, we pass the `author` attribute (which is coming from `PostHeader` Colocated Fragment) to the `Avatar` component through it `user`prop.

Now the `PostHeader` Colocated Fragment can be consumed the same way as we consumed the one from `Avatar`, through it `fragments` attribute.

**Creating a query by using a Colocated Fragment** 

Its time to use our Colocated Fragments to build the query that we gonna execute. In this example we gonna use [@apollo/client](https://www.apollographql.com/docs/react/) `useQuery` hook, but you should be able to use any GraphQL client library:

```jsx
// PostList.jsx
import { useQuery } from '@apollo/client';
import gql from 'graphql-tag';
import { PostHeader } from '../components/PostHeader';

const POST_LIST_QUERY = gql`
  query PostList {
    posts {
      ...PostHeader,
    }
  }
  ${PostHeader.fragments.post}
`;

export const PostList = () => {
  const { loading, error, data } = useQuery(POST_LIST_QUERY);
  if (loading) {
    return <div>Loading...</div>;
  }
  if (error || !data) {
    return <div>An error occurred</div>;
  }
  return (
    <div>
      <div>
        {data.posts.map((post) => (
          <PostHeader post={post} />
        ))}
      </div>
    </div>
  );
};
```

The query is built in the same way as we did for `PostHeader` and `Avatar` Colocated Fragments.

- First we import `PostHeader` component, which includes a `fragments`attribute. We add the Colocated Fragment through the string interpolation: `${PostHeader.fragments.post}` and we consume it by doing `...PostHeader` within the `posts` query body.
- Our query now includes all the defined fields in the `Avatar`and `PostHeader` Colocated Fragments.
- We execute  the `POST_LIST_QUERY` query through the `useQuery` hook from @apollo/client.
- Finally, the `posts` query returns an array. We iterate through the array and pass each of the elements to the `PostHeader` `post` prop.

This way, we successfully built our query in our parent component while keeping the required data next to the components that consume it.

## ¿Why use Colocated Fragments?

When using Colocated Fragments, our GraphQL-React data layer gets some of the React components benefits automatically:

- **High cohesion:** React components tend to have a [high cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)#High_cohesion) by nature, **rendering**, **styling** and **logic** layers normally are within the same file or folder. When you import a component, you don't worry about implementing any of these layers manually. By using Colocated Fragments, now you don't need to worry about how to get the needed data for the component. Your component now includes the **rendering**, **styling**, **logic** and **data** layers!
- **Low coupling:** Accomplishing a high cohesion between the component and the data gives us the extra benefit of low coupling between different components which helps with code **maintainability**.

    This might be clearer with an example. Let's say that our `Avatar` component now needs to render the user's Twitter handler. this change would look like this when using Colocated Fragments:

    ```jsx
    export const Avatar = ({ user }) => {
      return (
        <div>
          <a href={`/user/${user.id}`}>
            <h3>{user.name}</h3>
            {/* 1. in order to get access to this twitter attr */} 
            <h4>{user.twitter}</h4>
            <img src={user.image} />
          </a>
        </div>
      );
    };

    Avatar.fragments = {
      user: gql`
        fragment Avatar on User {
    	  id
          name
    	  twitter // 2. we only need to add this here
          image
      	}
      `
    };
    ```

    With Colocated Fragments, we only need to add the `twitter` field in the Fragment definition and that's it! We don't need to go and check that all the components consuming Avatar are updated to pass this new `twitter` attribute.

- **Composition:** When using Colocated Fragments, we build our queries the same way we build React components, through [composition](https://reactjs.org/docs/composition-vs-inheritance.html). Each fragment is treated as a piece of data that can be exported and **reused** by other fragments or queries.

## Extra (typescript): Generate your prop types automatically

If you use typescript, you get an extra benefit from using Colocated Fragments: automatic prop types generation for your components based on the Fragment fields!

Let's see how we do it with `yarn` ( `npm` works too)

- Install the @graphql-codegen required libraries:

    ```bash
    yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations
    ```

- In your React/typescript root folder execute:

    ```bash
    ./node_modules/.bin/graphql-codegen init
    ```

- Answer the CLI questions for generating the config file:
    - **What type of application are you building?** React
    - **Where is your schema?** filepath or url to your GraphQL Schema
    - **Where are your operations and fragments?** The path regex to your React components. Example: `./src/**/!(*.d).{ts,tsx}`
    - **Pick plugins**: Select `TypeScript` and `TypeScript Operations`
    - **Where to write the output**: The path where the prop types are going to be generated at. defaults to `src/generated/graphql.ts`
    - **Do you want to generate an introspection file?** n
    - **How to name the config file?** Config filename. defaults to `codegen.yml`
    - **What script in package.json should run the codegen?** The `package.json` script name to be created which will be used for generating the Fragment prop types. I use: `graphql-types`
- After completing the questions, you should see a new `codegen.yml`config file in your root folder. It should look like this:

    ```yaml
    overwrite: true
    schema: "http://localhost:4000"
    documents: "./src/**/!(*.d).{ts,tsx}"
    generates:
      src/generated/graphql.ts:
        plugins:
          - "typescript"
          - "typescript-operations"
    ```

- In your `package.json` now you should have a new command in the  `scripts` section:

    ```json
    "graphql-types": "graphql-codegen --config codegen.yml"
    ```

- Let's try it. Execute:

    ```bash
    yarn graphql-types
    ```

- If everything was set correctly, you should see a message like this:

    ```bash
    yarn graphql-types
    yarn run v1.22.4
    $ graphql-codegen --config codegen.yml
      ✔ Parse configuration
      ✔ Generate outputs
    ✨  Done in 2.18s.
    ```

- Now you should have a `src/generated/graphql.ts` file with all your Fragments and GraphQL Schema types. From our example, we get something like this:

    ```tsx
    ...
    export type User = {
      __typename?: 'User';
      id: Scalars['ID'];
      name: Scalars['String'];
      email?: Maybe<Scalars['String']>;
      image?: Maybe<Scalars['String']>;
      twitter?: Maybe<Scalars['String']>;
    };

    export type AvatarFragment = (
      { __typename?: 'User' }
      & Pick<User, 'id' | 'name' | 'image'>
    );
    ...
    ```

- If you can find your Fragment types, you are ready to start using them in your components:

    ```tsx
    // Avatar.tsx
    import gql from 'graphql-tag';
    import React from 'react';
    import { AvatarFragment } from '../generated/graphql';

    export interface AvatarProps {
    	user: AvatarFragment
    }
    export const Avatar = ({ user }: AvatarProps) => {
      return (
        <div>
          <a href={`/user/${user.id}`}>
            <h3>{user.name}</h3>
            <img src={user.image} />
          </a>
        </div>
      );
    };

    Avatar.fragments = {
      user: gql`
        fragment Avatar on User {
    	  id
          name
          image
      	}
      `
    };
    ```

- Done. Now every time you want to make a change to your Colocated Fragments, you just need to execute `yarn graphql-types`and your prop types will be updated automatically!

Finally, here are the github branches links to the Blog example. Each branch represents a different way on organizing your queries:

- [Colocated Fragments with automatic types generation Example](https://github.com/tenhsor1/tutorial-colocated-fragments-examples/tree/typed-fragments)
- [Colocated Fragments Example](https://github.com/tenhsor1/tutorial-colocated-fragments-examples/tree/untyped-fragments)
- [No Colocated Fragments Example](https://github.com/tenhsor1/tutorial-colocated-fragments-examples/tree/no-fragments)
- [Queries in a single file and no Colocated Fragments](https://github.com/tenhsor1/tutorial-colocated-fragments-examples/tree/single-file)

Happy composing!