DEV Community

Leny BERNARD
Leny BERNARD

Posted on • Updated on

Practical Guide - Leveraging TypeScript and GraphQL (fragment + ts pick + hooks)

Introduction

TypeScript and GraphQL have become staples in modern web development, providing significant benefits in efficiency, safety, and robustness. In this article, we will delve into how these two technologies can be combined using graphql-codegen, a powerful tool for automatically generating TypeScript types from GraphQL queries and mutations.

Why TypeScript and GraphQL?

TypeScript provides static typing for JavaScript, offering benefits such as predictability, readability and maintainability, and improved tooling and productivity.

GraphQL, on the other hand, allows efficient data loading by allowing clients to specify exactly what data they need. This reduces the chances of over-fetching or under-fetching data, common issues in REST APIs. GraphQL is also strongly typed, providing added safety and robustness to your APIs.

When TypeScript and GraphQL are used together, they offer a seamless, type-safe experience from front to back, supercharging the development process.

Optimizing TypeScript and GraphQL with graphql-codegen

One of the challenges when working with TypeScript and GraphQL is maintaining the TypeScript types that correspond to the GraphQL schema. This is where graphql-codegen comes in. It's a tool that automatically generates TypeScript types from your GraphQL queries, mutations, and subscriptions, ensuring your types are always up-to-date with your schema.

Here are some key practices when using graphql-codegen:

1. Fine-grained TypeScript Types

When writing GraphQL queries or mutations, try to be as specific as possible about the data you are fetching. Avoid fetching more data than necessary, and avoid reusing large, complex types when a smaller type will do.

For example, instead of:

query {
  user {
    id
    name
    email
    posts {
      id
      title
      content
      comments {
        id
        content
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Consider using:

query {
  user {
    id
    name
    email
  }
}
Enter fullscreen mode Exit fullscreen mode

and

query {
  posts {
    id
    title
    content
  }
}
Enter fullscreen mode Exit fullscreen mode

Separately. This will generate smaller, more specific TypeScript types that can be used more flexibly in your code.

2. Leveraging GraphQL Fragments

When a specific part of a query or mutation is reused in multiple places, consider splitting it into a fragment. Fragments are a way to create reusable pieces of GraphQL queries. You can also use fragments across different files, allowing you to further modularize your GraphQL queries and mutations.

Here's an example of how you can define a fragment:

fragment UserInfo on User {
  id
  name
  email
}
Enter fullscreen mode Exit fullscreen mode

Then, you can use this fragment in a query:

query {
  user {
    ...UserInfo
  }
}
Enter fullscreen mode Exit fullscreen mode

No need to import, just use the Fragment name you defined.

3. Fine-tune the types used in components

As we saw earlier, one of the interests of GraphQL will be to finely define the information that we want to retrieve from the API directly from the query.
For example, for the display of a component representing a user, if we only need the first name, the id and the avatar of a user, we do not want to retrieve all the information related to a user and that's what graphql allows us. But the problem is that the UserCard component cannot take a user in its props because we have chosen precisely the first name, the id and the avatar and there is a risk of having a typescript error if the component expects to have a User because the response to the graphql query will only represent a partial version of the object.

So to solve this problem, the UserCard component will define precisely what itneeds to work. To do this, we can use several Typescript mechanisms including Pick:

/* UserCard.types.ts */
import { User as FullUser } from '../../api.types'

export type User = Pick<
    FullUser,
    'id' | 'firstname'
> & {
    avatar?: {
        url: string
    }
}


export type UserCardProps = {
    user: User
} & CardProps
Enter fullscreen mode Exit fullscreen mode

This type "pick" only the id and firstname User properties and will union with a
custom version of the avatar objet which will only have the url property.

Then, UserCard will have to use it in props :

/* UserCard.tsx */
//... 
import { UserCard, UserCardProps } from './UserCard.types'

export const UserCard = ({
    user,
    ...otherProps
}: UserCardProps) => {
    return <Card>{/* something*/ }</Card>
}
Enter fullscreen mode Exit fullscreen mode

By precisely defining what our component needs, the partial user received in the
graphql response will be sufficient and if the component evolves and requires new
information from the user, then typescript will force us to change the UserCard.type.ts and update the graphql query to properly retrieve the new information !

Conclusion

TypeScript and GraphQL are powerful tools and graphql-codegen makes it even easier to use them together. By following these best practices, we can ensure that our TypeScript types are always in sync with our GraphQL schema, enhancing the predictability, maintainability, and productivity of our development process.

++

Top comments (0)