A CLI tool to generate individual GraphQL operation files from GraphQL APIs
npm install gqloperaA fast, focused CLI tool that extracts individual GraphQL operation files from any GraphQL API. Perfect for feeding into GraphQL Codegen or other GraphQL tooling.
- Introspects your GraphQL API
- Extracts all queries, mutations, and subscriptions
- Generates individual .graphql files with complete selection sets
- Organizes them in query/, mutation/, and subscription/ folders
- Ready for Codegen: Generated files work seamlessly with GraphQL Code Generator
Using generated hooks is no longer the recommended workflow.
- Recommended approach is to use Fragment Colocation to declare each component’s data requirements near the component itself.
- This approach keeps the number of data-fetching hooks low—ideally one per page—while avoiding network waterfalls and preventing overfetching..
- The current recommendation is to only generate TypedDocumentNodes and use them with Apollo Client’s useQuery, useSuspenseQuery, useFragment, and useSuspenseFragment hooks.
However, GQLOpera still can be used to :
- Quick project bootstrapping - rapidly generate a starting point for your GraphQL operations
- API exploration - understand your schema structure and available operations
- Migration projects - extract operations from existing APIs
- Documentation - create a reference of all available operations
``bashExtract operations from your GraphQL API
npx gqlopera generate --endpoint http://localhost:4000/graphql --output ./graphql
🔧 Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
|
endpoint | string | required | GraphQL API endpoint URL |
| output | string | "graphql" | Output directory path |
| headers | object | {} | HTTP headers for authentication |
| excludeTypes | string[] | [] | Type names to exclude from generation |
| watch | boolean | false | Watch for schema changes |
| maxDepth | number | 5 | Maximum nesting depth for selection sets (1-10) |
| maxFields | number | 50 | Maximum fields per type (5-100) |
| circularRefs | string | "skip" | Circular reference handling: "skip", "silent", or "allow" |
| shallowMode | boolean | false | Enable shallow mode (maxDepth=1) |
| includeFields | string[] | [] | Only include specific fields |
| excludeFields | string[] | [] | Exclude specific fields |
| fieldDepthMap | object | {} | Custom depth per type |
| circularRefDepth | number | 1 | Depth limit for circular references |
| circularRefTypes | object | {} | Custom depth per circular type |🎯 Shallow Data Options
For users who want minimal, focused data extraction (1-2 levels deep), GQLOpera provides several options:
$3
`bash
Generate only 1 level deep
gqlopera generate --endpoint http://localhost:4000/graphql --shallowOr via config
{
"shallowMode": true
}
`$3
`bash
Generate 2 levels deep
gqlopera generate --endpoint http://localhost:4000/graphql --max-depth 2Or via config
{
"maxDepth": 2
}
`$3
`bash
Only include specific fields
gqlopera generate --endpoint http://localhost:4000/graphql --include-fields "id,name,title,email"Or via config
{
"includeFields": ["id", "name", "title", "email"]
}
`$3
`json
{
"fieldDepthMap": {
"User": 2, // User type: 2 levels deep
"Post": 1, // Post type: 1 level deep
"Comment": 0 // Comment type: no nesting
}
}
`$3
`graphql
With maxDepth: 1
query GetUser($id: ID!) {
getUser(id: $id) {
id # Level 1 - scalar
name # Level 1 - scalar
email # Level 1 - scalar
profile # Level 1 - object (no nesting)
posts # Level 1 - array (no nesting)
}
}With includeFields: ["id", "name"]
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
}
}
`🎯 Circular Reference Depth Control
For schemas with circular references (like User ↔ Post), GQLOpera provides options to extract limited data instead of completely skipping:
$3
`bash
Allow circular refs with 2 levels of depth
gqlopera generate --endpoint http://localhost:4000/graphql --circular-refs allow --circular-ref-depth 2Or via config
{
"circularRefs": "allow",
"circularRefDepth": 2
}
`$3
`json
{
"circularRefs": "allow",
"circularRefTypes": {
"User": 2, // User circular refs: 2 levels deep
"Post": 1, // Post circular refs: 1 level deep
"Comment": 0 // Comment circular refs: no nesting
}
}
`$3
`graphql
Before: Circular ref completely skipped
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
posts {
id
title
author # Circular reference to User skipped
}
}
}After: Circular ref with limited depth
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
posts {
id
title
content
author {
id # Circular ref allowed with depth limit
name # Only scalar fields included
email # No further nesting
}
}
}
}
`$3
- User ↔ Post relationships: Get basic user info in posts
- Organization ↔ Department: Show department hierarchy
- Category ↔ Product: Display category info in products
- Any bidirectional relationships: Extract useful data without infinite loops🎯 Output Structure
`
graphql/
├── query/
│ ├── getUsers.graphql
│ ├── getProfile.graphql
│ └── searchPosts.graphql
├── mutation/
│ ├── createUser.graphql
│ ├── updateProfile.graphql
│ └── deletePost.graphql
└── subscription/
└── messageUpdates.graphql
`📄 Generated Files
Each file contains a complete, executable GraphQL operation:
`graphql
query/getUsers.graphql
query GetUsers($offset: Int!, $limit: Int!) {
getUsers(offset: $offset, limit: $limit) {
items {
id
username
email
profile {
firstName
lastName
avatar
}
}
totalCount
}
}
`⚙️ Configuration
Create a
gqlopera.config.json file:`json
{
"endpoint": "http://localhost:4000/graphql",
"output": "graphql",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
},
"excludeTypes": ["InternalType"],
"watch": false,
"maxDepth": 5,
"maxFields": 50,
"circularRefs": "skip"
}
`Or generate one interactively:
`bash
gqlopera init
`🔄 Handling Complex Schemas
GQLOpera intelligently handles complex GraphQL schemas with circular reference detection and depth management:
$3
GraphQL schemas often contain circular references where types reference each other, creating potential infinite loops. GQL Operations uses tracking to detect and handle these safely:
Detection Strategy:
`graphql
Before: Infinite loop potential
type User {
id: ID!
posts: [Post!]!
}type Post {
id: ID!
author: User! # Circular reference back to User
}
After: Smart handling with comments
query GetUser($id: ID!) {
getUser(id: $id) {
id
name
email
posts {
id
title
content
author # Circular reference to User skipped
}
}
}
`$3
Depth Tracking Algorithm:
- Level 0: Root operation field
- Level 1: Direct properties of root
- Level 2: Nested objects within properties
- Level 3+: Deep nesting controlled by
maxDepthExample with maxDepth: 5:
`graphql
query GetOrganization($id: ID!) {
getOrganization(id: $id) { # Level 0
id # Level 1 - scalar
name # Level 1 - scalar
departments { # Level 1 - object
id # Level 2 - scalar
name # Level 2 - scalar
manager { # Level 2 - object
id # Level 3 - scalar
profile { # Level 3 - object
settings { # Level 4 - object
theme # Level 5 - scalar
preferences # Max depth (5) reached
}
}
}
}
}
}
`$3
Granular Control:
`json
{
"maxDepth": 7, // Allow deeper nesting for complex schemas
"maxFields": 25, // Balance between completeness and readability
"circularRefs": "skip" // Have control here
}
`
$3
The
maxFields setting prevents operations from becoming too large by limiting how many fields are included per GraphQL type.⚠️ Important: No Smart Prioritization
- Takes the first N fields in schema definition order
- No prioritization of "important" fields like
id or name
- No filtering of "less important" fields like timestamps
- Field order depends entirely on how the GraphQL server defines its schemaExample with
maxFields: 5:`graphql
Original User type has 12 fields
query GetUser($id: ID!) {
user(id: $id) {
id # Field 1 ✅ Included
email # Field 2 ✅ Included
firstName # Field 3 ✅ Included
lastName # Field 4 ✅ Included
avatar # Field 5 ✅ Included
# ... 7 more fields (limited by maxFields: 5)
# ❌ These fields were dropped:
# createdAt, updatedAt, profile, preferences,
# organization, permissions, lastLoginAt
}
}
`When Field Limiting Activates:
- User type has 50+ fields → Only first 50 included
- Product type has 100+ fields → Only first 100 included
- Automatically adds comment showing how many fields were omitted
Better Alternatives:
- Use
excludeTypes to skip problematic types entirely
- Increase maxFields if you need more fields
- Use maxDepth to control nesting instead of field countExample Output:
`graphql
query GetProduct($id: ID!) {
getProduct(id: $id) {
id # Always included - primary key
name # Essential scalar fields
price
description
category {
id # Simple reference
name
parent # Circular reference to Category skipped
}
reviews {
# ... 15 more fields (limited by maxFields: 20)
}
}
}
``🛠️ CLI Commands
$3
`bash
gqlopera generate [options]Options:
-e, --endpoint GraphQL endpoint URL
-o, --output Output directory (default: "graphql")
-c, --config Config file path (default: "gqlopera.config.json")
-h, --headers HTTP headers as JSON string
--watch Watch for schema changes and regenerate
--verbose Enable verbose logging
--max-depth Maximum depth for field expansion (default: 5)
--max-fields Maximum fields per type (default: 50)
--shallow Enable shallow mode (maxDepth=1)
--include-fields Comma-separated list of fields to include
--exclude-fields Comma-separated list of fields to exclude
--circular-ref-depth Depth limit for circular references (default: 1)
--circular-refs Circular reference handling: skip, silent, or allow (default: skip)
`$3
`bash
gqlopera init
`$3
`bash
gqlopera validate
`🔗 Perfect for GraphQL Codegen
Use with GraphQL Code Generator to generate types and hooks:
`yaml
codegen.yml
overwrite: true
schema: "http://localhost:4000/graphql"
documents: "./graphql/*/.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
``- Ready for Codegen: Generated document files work directly with GraphQL Code Generator
- Testing: Use individual operations in your tests
- Documentation: Clean, organized operation files
- Migration: Extract operations from existing APIs
- Development: Better organization of GraphQL operations
- Node.js 16+
- Access to a GraphQL endpoint (with introspection enabled)
GPL-2.0
---
Extract → Organize → Generate 🚀