A 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
.graphqlfiles with complete selection sets - Organizes them in
query/,mutation/, andsubscription/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βsuseQuery,useSuspenseQuery,useFragment, anduseSuspenseFragmenthooks.
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
# Extract operations from your GraphQL API
npx gqlopera generate --endpoint http://localhost:4000/graphql --output ./graphql
# Or install globally
npm install -g gqlopera
gqlopera generate --endpoint https://api.example.com/graphql --output ./my-graphql| 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 |
For users who want minimal, focused data extraction (1-2 levels deep), GQLOpera provides several options:
# Generate only 1 level deep
gqlopera generate --endpoint http://localhost:4000/graphql --shallow
# Or via config
{
"shallowMode": true
}# Generate 2 levels deep
gqlopera generate --endpoint http://localhost:4000/graphql --max-depth 2
# Or via config
{
"maxDepth": 2
}# 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"]
}{
"fieldDepthMap": {
"User": 2, // User type: 2 levels deep
"Post": 1, // Post type: 1 level deep
"Comment": 0 // Comment type: no nesting
}
}# 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
}
}For schemas with circular references (like User β Post), GQLOpera provides options to extract limited data instead of completely skipping:
# Allow circular refs with 2 levels of depth
gqlopera generate --endpoint http://localhost:4000/graphql --circular-refs allow --circular-ref-depth 2
# Or via config
{
"circularRefs": "allow",
"circularRefDepth": 2
}{
"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
}
}# 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
}
}
}
}- 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
graphql/
βββ query/
β βββ getUsers.graphql
β βββ getProfile.graphql
β βββ searchPosts.graphql
βββ mutation/
β βββ createUser.graphql
β βββ updateProfile.graphql
β βββ deletePost.graphql
βββ subscription/
βββ messageUpdates.graphql
Each file contains a complete, executable GraphQL operation:
# query/getUsers.graphql
query GetUsers($offset: Int!, $limit: Int!) {
getUsers(offset: $offset, limit: $limit) {
items {
id
username
email
profile {
firstName
lastName
avatar
}
}
totalCount
}
}Create a gqlopera.config.json file:
{
"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:
gqlopera initGQLOpera intelligently handles complex GraphQL schemas with circular reference detection and depth management:
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:
# 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
}
}
}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
maxDepth
Example with maxDepth: 5:
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
}
}
}
}
}
}Granular Control:
{
"maxDepth": 7, // Allow deeper nesting for complex schemas
"maxFields": 25, // Balance between completeness and readability
"circularRefs": "skip" // Have control here
}The maxFields setting prevents operations from becoming too large by limiting how many fields are included per GraphQL type.
- Takes the first N fields in schema definition order
- No prioritization of "important" fields like
idorname - No filtering of "less important" fields like timestamps
- Field order depends entirely on how the GraphQL server defines its schema
Example with maxFields: 5:
# 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
excludeTypesto skip problematic types entirely - Increase
maxFieldsif you need more fields - Use
maxDepthto control nesting instead of field count
Example Output:
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
### Generate Operations
```bash
gqlopera generate [options]
Options:
-e, --endpoint <url> GraphQL endpoint URL
-o, --output <path> Output directory (default: "graphql")
-c, --config <path> Config file path (default: "gqlopera.config.json")
-h, --headers <json> HTTP headers as JSON string
--watch Watch for schema changes and regenerate
--verbose Enable verbose logging
--max-depth <number> Maximum depth for field expansion (default: 5)
--max-fields <number> Maximum fields per type (default: 50)
--shallow Enable shallow mode (maxDepth=1)
--include-fields <fields> Comma-separated list of fields to include
--exclude-fields <fields> Comma-separated list of fields to exclude
--circular-ref-depth <number> Depth limit for circular references (default: 1)
--circular-refs <mode> Circular reference handling: skip, silent, or allow (default: skip)
gqlopera initgqlopera validateUse with GraphQL Code Generator to generate types and hooks:
# 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 π