DEV Community

Cover image for The fragment-based Apollo Client
Julio Xavier
Julio Xavier

Posted on

The fragment-based Apollo Client

The days that I had a hard time maintaining codebases with Apollo GraphQL are behind me. It all changed when I adopted a single concept.

Apollo Client (a.k.a. Apollo GraphQL) is a GraphQL client used for consuming GraphQL data in web and mobile apps. It has caching, tracks network status, and does a fair amount of heavy lifting so developers can focus on building the product.

As the app grows and becomes more complex, it can get hard to know where the data is coming from and what piece of UI needs that data. What can be done to solve this is colocating components and fragments.

Declaring the fragment

Let's say that we have a component that it's responsible for displaying a card with information about a user:

// notice the component name, it will be used in the fragment
export const UserCard = ({ user }) => {
  return (
    <div>
      <h1>{user.name}</h1>
      <div>
        <img src={user.profile_picture?.uri} />
      </div>
    </div>
  );
};

// attach a fragments object to your component, more on that later
UserCard.fragments = {
  // notice that this is the name of the GraphQL type, using camel-case
  // the fragment name follows this pattern to avoid conflicts: <Component Name>_<GraphQL Type>
  // so in this case, the fragment name is UserCard_User
  user: gql`
    fragment UserCard_User on User {
      name
      profile_picture {
        uri
      }
    }
  `,
};
Enter fullscreen mode Exit fullscreen mode

Using the fragment

Now, we want to use this card on a page that we're building:

import { UserCard } from '../components/UserCard';

const QUERY = gql`
  query UserPage {
    user(id: 200) {
      id
      # spread the fragment here, so it's included in the query
      ...UserCard_User
    }
  }
  ${UserCard.fragments.user}
`;

const UserPage = () => {
  const { data } = useQuery(QUERY);

  return (
    <div>
      <h1>Some nice title</h1>
      {/* pass the user data to the component */}
      <UserCard user={data.user} />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

And that's all! The data declared in the UserCard will also be included in the query and the page just needs to forward it to the component.

Updating the fragment

So, let's say that after a few weeks the Product Manager comes back and says:

"Hey, we discovered that it's important to see the user last name as well. Can you add it?"

Sure thing! That's what we have to do:

export const UserCard = ({ user }) => {
  return (
    <div>
      {/* add the last name in the UI */}
      <h1>
        {user.name} {user.last_name}
      </h1>
      <div>
        <img src={user.profile_picture?.uri} />
      </div>
    </div>
  );
};

UserCard.fragments = {
  user: gql`
    fragment UserCard_User on User {
      name
      # add the "last name" to the fragment
      last_name
      profile_picture {
        uri
      }
    }
  `,
};
Enter fullscreen mode Exit fullscreen mode

So with just two lines of code, all the places that are using this card will be updated and have the right data. No more updating each query and passing down props. 🚀

Bonus: TypeScript

It gets even better with TypeScript, as when the types are generated, they're also colocated with the component:

import { UserCard_User } from './__generated__/UserCard_User';

type Props = {
  user: UserCard_User;
};

export const UserCard = ({ user }: Props) => {
  return (
    <div>
      {/* add the last name in the UI */}
      <h1>
        {user.name} {user.last_name}
      </h1>
      <div>
        <img src={user.profile_picture?.uri} />
      </div>
    </div>
  );
};

UserCard.fragments = {
  user: gql`
    fragment UserCard_User on User {
      name
      # add the "last name" to the fragment
      last_name
      profile_picture {
        uri
      }
    }
  `,
};
Enter fullscreen mode Exit fullscreen mode

Thanks for reading!

What are your thoughts on using Apollo with fragments? How would you change it?

Let's keep in touch! Here's my Twitter.

References

https://www.apollographql.com/docs/react/data/fragments/
https://relay.dev/docs/guided-tour/rendering/fragments
https://kentcdodds.com/blog/colocation

Top comments (1)

Collapse
 
jgcmarins profile image
João Marins

What an awesome approach!