Adding Static Typing to 381 GraphQL Queries with Codemods

The Context

We have a GraphQL API to connect our fronted to our backend service on one of our projects. We recently ran into a bug where a developer changed the contract on the backend without updating one of the related queries on the frontend.

The Problem

Static typing would have helped, but all our GQL queries are written in .gql files, which doesn’t directly integrate with TypeScript. We have used GraphQL Code Generator in the past, which is designed to solve this problem by hitting your schema endpoint and generating TypeScript types which you can use to confirm the contract at build-time.
We had 381 of these files and are currently doing a number of tech maintenance pieces on the system - at first glance adding the overhead of another incremental migration seemed like it wasn’t worth the cost right now.
It looked like if we wanted static typing on our projects, we would be in for a long-haul incremental refactor.

The Solution

We noticed that with a few concessions, the bulk of this refactor could be done programatically!
We would migrate the raw SomeQuery.gql files to be SomeQuery.gql.ts files which consume the codegen types and therefore pass that on to the return type of useQuery (from Apollo client), giving us type-safe GraphQL queries!
We needed to:
  1. use default exports from our new .ts files, as that is how the .gql query files are currently consumed within the app.
  1. use an absolute import to get graphql (provided by codegen) within the new .ts files, as they all sit at different levels within the directory structure.
The places importing the files don’t need to change as adding a .ts extension is implicit in our module imports. The files we wanted to convert were all named as XXXX.gql, so it was easy to select those files we needed to migrate all in one go.
We managed to migrate 381 queries and mutations in an afternoon, which might have otherwise taken months as an incremental initiative!

The Code

Here’s the PowerShell script we used to do the migration, which took an afternoon rather than days or months.
function migrate_files() { local old_path=$1 local new_path=$old_path.ts mv $old_path $new_path local content=$(cat $new_path) cat > $new_path <<EOF import { graphql } from "graphql/codegen"; const query = graphq(\` $content \`); export default query; EOF } for file in $(find app -name '*.gql') do migrate_file $file done pnpm test:format:fix