TL;DR — rgql is a CLI tool that renames GraphQL types, fields, and fragments across schema files and embedded TypeScript queries using full AST analysis. Because it's type-safe and deterministic, it's ideal for AI coding agents like Claude Code and Codex — they can invoke a single command instead of performing fragile find-and-replace across dozens of files.
The Problem: Renaming in GraphQL Is Surprisingly Hard
Imagine you have a User type with a firstName field, and a Product type that also has a firstName field. You want to rename User.firstName to User.fullName.
A naive find-and-replace would touch both. Even a regex-aware approach struggles when the same field name appears in nested selections:
query GetDashboard {
user {
firstName # ← should be renamed
orders {
product {
firstName # ← should NOT be renamed
}
}
}
}
Now multiply this across .graphql schema files and graphql tagged templates embedded in .ts/.tsx files. Manual refactoring is tedious and error-prone. IDE support for GraphQL refactoring is limited — most editors treat GraphQL strings as opaque text.
The Solution: AST-Aware, Type-Safe Refactoring
rgql solves this with two key GraphQL-js primitives: TypeInfo and visitWithTypeInfo.
When renaming User.firstName → User.fullName, rgql:
- Parses your schema to build a full type graph
- Walks every query/mutation/fragment using
visitWithTypeInfo, which tracks the parent type at each field node - Only renames a
firstNamefield whentypeInfo.getParentType()resolves toUser
This means Product.firstName is never touched — not because of text heuristics, but because the tool understands the schema semantics.
# Dry run — preview changes without modifying files
rgql rename field User.firstName User.fullName
# Apply
rgql rename field User.firstName User.fullName --write
Output:
Changes:
schema/user.graphql:12 firstName → fullName [schema]
src/components/UserCard.tsx:34 firstName → fullName (graphql`...`) [document]
2 files, 2 occurrences
Dry run. Use --write to apply.
Why This Matters for AI Agents
Here's the angle I'm most excited about: rgql is designed to be used by AI coding agents.
When you ask Claude Code or Codex to rename a GraphQL field, the agent typically has to:
- Find the schema definition
- Find all query files referencing that field
- Determine which
firstNamebelongs to which type - Apply changes to each file individually
- Hope it didn't miss anything or break something
This consumes a lot of context window — the agent needs to read and reason about every file. And even smart agents can miss edge cases like inline fragments or interface implementations.
With rgql, the agent just runs:
rgql rename field User.firstName User.fullName --write
One command. Zero context consumed. Guaranteed correctness.
The agent doesn't need to read any GraphQL files. It doesn't need to understand query nesting. The AST does the work. This is the same philosophy behind tools like gopls rename or ts-morph — give agents safe, atomic refactoring primitives instead of asking them to do text surgery.
Interface Safety — Even Agents Can't Break This
When renaming a field that's defined on an interface, rgql detects the impact:
⚠️ Breaking change detected:
'firstName' is defined on interface 'Node'
The following types implement this interface:
- User.firstName
- Admin.firstName
Rename all of the above together? [y/n/q]
Without --force, it refuses to apply a partial rename that would break your schema. This guardrail protects both humans and AI agents from introducing silent inconsistencies.
Architecture: Pure Core / Impure Shell
rgql follows a Functional Domain Modeling architecture:
src/
├── core/ # Pure functions — no IO, no side effects
│ ├── pipeline.ts # computeRenamePlan() → RenameOutcome
│ ├── validate.ts # Type/field existence, collision checks
│ ├── rename-schema.ts # AST rename in .graphql files
│ └── rename-document.ts # AST rename in queries (standalone + embedded)
├── shell/ # IO boundary — the only place with side effects
│ ├── cli.ts # handleOutcome() — console output, file writes
│ ├── config.ts # graphql-config loading
│ └── file-system.ts # File I/O
└── types/
├── domain.ts # Branded types, discriminated unions
└── result.ts # Result<T, E> instead of exceptions
The entire rename pipeline is a pure function:
RenameCommand + Schema + Documents → RenameOutcome
RenameOutcome is a discriminated union with 7 variants — dry-run, written, no-changes, validation-error, interface-skipped, interface-aborted, interactive-complete. The shell layer pattern-matches on the variant and performs the appropriate IO.
This makes the core logic trivially testable and predictable — which is exactly what you want when an AI agent is deciding whether to call your tool.
Branded Types Prevent Mixing
TypeScript lets you accidentally pass a type name where a field name is expected — they're both string. rgql prevents this at compile time:
type TypeName = string & { readonly __brand: "TypeName" };
type FieldName = string & { readonly __brand: "FieldName" };
const toTypeName = (value: string): TypeName => value as TypeName;
No runtime cost. Full compile-time safety.
Result Over Exceptions
Instead of throw, every function returns a Result:
type Result<T, E> =
| { readonly ok: true; readonly value: T }
| { readonly ok: false; readonly error: E };
This makes error paths explicit and composable with mapResult / flatMapResult. No hidden control flow.
Embedded Query Detection
rgql doesn't just handle standalone .graphql files. It uses ts-morph (TypeScript AST) to find graphql and gql tagged template literals inside .ts and .tsx files:
// rgql finds and correctly renames inside this:
const GET_USER = graphql`
query GetUser {
user {
firstName # ← renamed to fullName
}
}
`;
The offset arithmetic is handled internally — GraphQL AST positions are relative to the query string, but rgql translates them to absolute file positions for precise text replacement.
Getting Started
# Install (requires Bun)
bun install
bun run build # produces ./rgql single binary
# Configuration — uses standard graphql-config
# graphql.config.yml
schema: ./schema/**/*.graphql
documents: ./src/**/*.{ts,tsx}
Three rename commands:
rgql rename type User Account # Rename a type
rgql rename field User.firstName User.fullName # Rename a field
rgql rename fragment UserBasic UserSummary # Rename a fragment
Flags:
| Flag | Description |
|---|---|
--write |
Apply changes (default: dry run) |
-i |
Interactive — confirm each change |
--force |
Allow interface cascade renames |
--config |
Explicit config file path |
--project |
Project name for monorepo configs |
What's Next
- Support
/* GraphQL */and# graphqlcomment-tagged queries -
rename enum-valuecommand - Watch mode for continuous refactoring
- npm / Homebrew distribution
Try It Out
GitHub: https://github.com/Yamashou/rgql
If you're using GraphQL with TypeScript and tired of manual renames — or if you're building AI agent workflows that need safe refactoring primitives — give rgql a try.
Built with Bun, graphql-js, ts-morph, and Commander.js.
Top comments (0)