How to Use graphql-codegen, A Beginners' Guide

Maurice Odida

Maurice Odida

20 June 2025

How to Use graphql-codegen, A Beginners' Guide

In the ever-evolving landscape of web development, GraphQL has emerged as a powerful alternative to traditional REST APIs, offering clients the ability to request precisely the data they need. However, this flexibility can introduce a new set of challenges, particularly when it comes to maintaining type safety between the frontend and backend. This is where graphql-codegen comes in, a revolutionary tool that automates the generation of typed code from your GraphQL schema, supercharging your development workflow and eliminating a whole class of runtime errors.

This article will serve as your guide to understanding and mastering graphql-codegen. We will start with the fundamental concepts, walk through a practical, step-by-step example of setting up and configuring the tool, and explore best practices for integrating it into your projects. By the end of this guide, you'll be equipped to leverage graphql-codegen to build more robust, maintainable, and type-safe applications.

💡
Want a great API Testing tool that generates beautiful API Documentation?

Want an integrated, All-in-One platform for your Developer Team to work together with maximum productivity?

Apidog delivers all your demands, and replaces Postman at a much more affordable price!
button

What is graphql-codegen and Why Do You Need It?

At its core, graphql-codegen is a command-line tool that introspects your GraphQL schema and your GraphQL operations (queries, mutations, and subscriptions) and generates code in a variety of languages. For the purpose of this beginner's guide, we will focus on its most popular use case: generating TypeScript types and hooks for frontend applications.

The primary problem that graphql-codegen solves is the tedious and error-prone process of manually writing TypeScript interfaces for your GraphQL API's data structures and the results of your queries. Without it, developers are left to either work with untyped data (losing the benefits of TypeScript) or spend significant time creating and maintaining types that can easily become outdated as the API evolves.

The benefits of adopting graphql-codegen are manifold:

This approach aligns perfectly with the "schema-first" development philosophy, where the GraphQL schema serves as the single source of truth for your API.

Getting Started: Your First graphql-codegen Setup

Let's dive into the practical aspects of using graphql-codegen. For this tutorial, we'll assume you have a basic understanding of GraphQL, TypeScript, and have Node.js and a package manager (like npm or yarn) installed.

Our goal is to set up graphql-codegen for a simple React application that displays a list of blog posts.

Step 1: Project Initialization and Dependencies

First, let's create a new React project with TypeScript and install the necessary dependencies.Bash

npx create-react-app my-blog --template typescript
cd my-blog
npm install @apollo/client graphql
npm install -D @graphql-codegen/cli @graphql-codegen/client-preset typescript

Here's a breakdown of the dependencies we've installed:

Step 2: The graphql-codegen Initialization Wizard

The easiest way to get started with graphql-codegen is by using its initialization wizard. Run the following command in your project's root directory:Bash

npx graphql-codegen init

The wizard will ask you a series of questions to help configure your project. Here's a typical set of answers for our blog application scenario:

After answering these questions, the wizard will create a codegen.ts file in your project's root and add a codegen script to your package.json.

Step 3: Understanding the Configuration File (codegen.ts)

The codegen.ts file is the heart of your graphql-codegen setup. Let's take a look at a simplified version of what the wizard generates:TypeScript

import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  overwrite: true,
  schema: "https://swapi-graphql.netlify.app/.netlify/functions/index",
  documents: "src/**/*.tsx",
  generates: {
    "src/gql/": {
      preset: "client",
      plugins: []
    }
  }
};

export default config;

Let's break down the key configuration options:

Step 4: Writing Your First GraphQL Query

Now that graphql-codegen is configured, let's write a GraphQL query to fetch our blog posts. Create a new file src/components/Posts.tsx:TypeScript

import { gql } from '../gql/gql';
import { useQuery } from '@apollo/client';

const GET_POSTS = gql(`
  query GetPosts {
    allFilms {
      films {
        id
        title
        director
        releaseDate
      }
    }
  }
`);

const Posts = () => {
  const { loading, error, data } = useQuery(GET_POSTS);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return (
    <div>
      {data?.allFilms?.films?.map((film) => (
        <div key={film?.id}>
          <h2>{film?.title}</h2>
          <p>Director: {film?.director}</p>
          <p>Release Date: {film?.releaseDate}</p>
        </div>
      ))}
    </div>
  );
};

export default Posts;

Notice that we're importing a gql function from the generated src/gql/gql.ts file. This is the gql function provided by the client-preset and it's what enables the magic of type inference.

Step 5: Running graphql-codegen and Seeing the Magic

Now, run the codegen script from your package.json:Bash

npm run codegen

This command will introspect the schema, find your GET_POSTS query, and generate the corresponding TypeScript types in the src/gql directory.

If you now inspect the data object returned by the useQuery hook in Posts.tsx, you'll see that it's fully typed! Your IDE will provide autocompletion for data.allFilms.films, and TypeScript will warn you if you try to access a field that doesn't exist in the query.

A Deeper Dive: A Practical Blog Frontend Example

Let's expand on our example to solidify your understanding. Imagine our blog has a more traditional schema:GraphQL

type Author {
  id: ID!
  name: String!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: Author!
}

type Query {
  posts: [Post!]!
  post(id: ID!): Post
}

type Mutation {
  createPost(title: String!, content: String!, authorId: ID!): Post!
}

Let's assume this schema is running on http://localhost:4000/graphql. We would update our codegen.ts accordingly:TypeScript

// codegen.ts
// ...
schema: "http://localhost:4000/graphql",
// ...

Now, let's create a component to display a single post and another to create a new one.

Fetching a Single PostTypeScript

// src/components/Post.tsx
import { gql } from '../gql/gql';
import { useQuery } from '@apollo/client';
import { useParams } from 'react-router-dom';

const GET_POST = gql(`
  query GetPost($id: ID!) {
    post(id: $id) {
      id
      title
      content
      author {
        id
        name
      }
    }
  }
`);

const Post = () => {
  const { id } = useParams<{ id: string }>();
  const { loading, error, data } = useQuery(GET_POST, {
    variables: { id },
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return (
    <div>
      <h2>{data?.post?.title}</h2>
      <p>By {data?.post?.author?.name}</p>
      <p>{data?.post?.content}</p>
    </div>
  );
};

export default Post;

After running npm run codegen, the types for the GetPostQuery and its variables will be generated, providing type safety for the useQuery hook and its result.

Creating a New PostTypeScript

// src/components/CreatePost.tsx
import { gql } from '../gql/gql';
import { useMutation } from '@apollo/client';
import { useState } from 'react';

const CREATE_POST = gql(`
  mutation CreatePost($title: String!, $content: String!, $authorId: ID!) {
    createPost(title: $title, content: $content, authorId: $authorId) {
      id
    }
  }
`);

const CreatePost = () => {
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
  const [createPost, { data, loading, error }] = useMutation(CREATE_POST);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    createPost({ variables: { title, content, authorId: '1' } }); // Assuming authorId '1' for simplicity
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Title"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
      />
      <textarea
        placeholder="Content"
        value={content}
        onChange={(e) => setContent(e.target.value)}
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Create Post'}
      </button>
      {error && <p>Error creating post: {error.message}</p>}
      {data && <p>Post created with ID: {data.createPost.id}</p>}
    </form>
  );
};

export default CreatePost;

Here, graphql-codegen generates the types for the CreatePostMutation and its variables. This ensures that when you call the createPost function, you are passing the correct types for title, content, and authorId.

Advanced Concepts and Best Practices

As you become more comfortable with graphql-codegen, you'll encounter more advanced features and best practices:

Fragments: graphql-codegen has excellent support for GraphQL fragments. You can define fragments in your .tsx files and graphql-codegen will correctly handle the types, promoting reusable and co-located data dependencies for your components.

Using .graphql Files: For better separation of concerns, you can write your GraphQL queries, mutations, and fragments in dedicated .graphql files. Simply update the documents array in your codegen.ts to include this file extension: documents: ["src/**/*.tsx", "src/**/*.graphql"].

Custom Scalars: If your GraphQL schema uses custom scalars (e.g., Date, JSON), you can map them to corresponding TypeScript types in your codegen.ts file to maintain type safety.

Watch Mode: For a seamless development experience, you can run graphql-codegen in watch mode. This will automatically regenerate your types whenever you save a file containing a GraphQL operation. Simply add a --watch flag to your codegen script in package.json: "codegen": "graphql-codegen --watch".

Troubleshooting Common Issues

While graphql-codegen is a powerful tool, you might encounter a few common issues as a beginner:

Conclusion: Embrace the Type-Safe Future

graphql-codegen is more than just a code generation tool; it's a paradigm shift in how we build applications with GraphQL. By embracing automation and leveraging the power of your schema, you can create more robust, maintainable, and enjoyable-to-work-with codebases.

This tutorial has provided you with a solid foundation for getting started with graphql-codegen. We've covered the core concepts, walked through a practical example, and touched upon best practices. The journey doesn't end here. The graphql-codegen ecosystem is vast, with a rich collection of plugins that cater to various frameworks and use cases. I encourage you to explore the official documentation, experiment with different plugins, and discover how graphql-codegen can revolutionize your GraphQL development workflow. Happy coding!

💡
Want a great API Testing tool that generates beautiful API Documentation?

Want an integrated, All-in-One platform for your Developer Team to work together with maximum productivity?

Apidog delivers all your demands, and replaces Postman at a much more affordable price!
button

Explore more

Why I Love Stripe Docs (API Documentation Best Practices)

Why I Love Stripe Docs (API Documentation Best Practices)

As a developer, I’ve had my fair share of late nights fueled by frustration and bad documentation. I think we all have. I can still vividly recall the cold sweat of trying to integrate a certain legacy payment processor years ago. It was a nightmare of fragmented guides, conflicting API versions, and a dashboard that felt like a labyrinth designed by a committee that hated joy. After hours of wrestling with convoluted SOAP requests and getting absolutely nowhere, I threw in the towel. A colleagu

20 June 2025

How to Install and Configure MongoDB MCP Server

How to Install and Configure MongoDB MCP Server

In the ever-evolving landscape of software development, the integration of Artificial Intelligence is no longer a futuristic concept but a present-day reality. AI-powered tools are rapidly becoming indispensable for developers, streamlining workflows, and enhancing productivity. Recognizing this trend, MongoDB has introduced a groundbreaking tool that bridges the gap between your database and AI: the MongoDB Model Context Protocol (MCP) Server. This tutorial provides a comprehensive, step-by-ste

20 June 2025

How to Use Sentry MCP Server for Debugging

How to Use Sentry MCP Server for Debugging

Developers are constantly under pressure to deliver high-quality applications with fewer bugs. While tools like Sentry have become indispensable for error and performance monitoring, the process of debugging can still be a time-consuming and complex endeavor. However, a new paradigm is emerging, one that promises to revolutionize the way we debug our applications by seamlessly integrating the power of artificial intelligence into our development workflows. At the forefront of this transformation

20 June 2025

Practice API Design-first in Apidog

Discover an easier way to build and use APIs