Organizing GraphQL files with components using near-operation-file pattern
Now that you know how to write GraphQL operations, let's discuss where to organize them. Colocation keeps your GraphQL files next to the components that use them, improving discoverability and maintainability.
What Colocation Means
In our GraphQL setup, "colocation" refers to the near-operation-file preset pattern where:
.graphql files are placed in the same folder as the components that use them
Generated TypeScript files (.graphql.ts) are created alongside the .graphql files
This is NOT about embedding GraphQL strings directly in component files
Discoverability: a screen's operations sit next to its root, and a component's fragment sits next to the component
Maintainability: Easy to update a fragment when modifying its component
Type Safety: Generated types are imported from the same directory
Modularity: Components are self-contained with their data requirements
Refactoring: Moving a component moves its fragment
Before and After Examples
❌ Don't do this - Centralized Operations
Old Pattern: All operations in a central location
Problems:
Operations disconnected from components
Hard to find what queries a component uses
Refactoring requires hunting down imports
No clear ownership of operations
Leads to bloated operations files that request too much data and query too many levels deep.
Types become difficult to reason about and maintain.
✅ Do this - Colocated Operations
New Pattern: The root's operations next to the root, each component's fragment next to the component
Benefits:
Clear ownership and organization
Easy to see a component's data needs
One request per screen, with no component fetching on its own
Types are scoped to the component and are easy to reason about
How Generated Files Work
When codegen runs (at the start of pnpm dev, or manually with pnpm turbo run codegen), the near-operation-file preset:
Finds all .graphql files in your app
Generates a .graphql.ts file next to each one
Includes typed document nodes and operation types
Example Generated Output
For this operation:
Generates this TypeScript file:
Important: NOT Inline GraphQL
❌ Don't do this - Inline GraphQL Strings
✅ Do this - Separate .graphql Files
Organizing a Complex Query Root
A root with several operations keeps them in one folder:
If the root is also a component that reads its own fields, it has a fragments.graphql like any other component.
Best Practices
✅ Do
Place .graphql files next to the code that uses them, under src/ of an app, remote, or package
Put a screen's operations next to its query root, and each component's fragment next to the component
Group related operations in the same file
❌ Don't
Put GraphQL strings directly in component files (pnpm lint:graphql rejects gql imports and tags outside tests)
Put a .graphql file anywhere codegen does not read, such as a remote's exposes/ directory
Name a document anything but queries.graphql, mutations.graphql, subscriptions.graphql, or fragments.graphql
Create deeply nested GraphQL folders
Share component-specific operations
Manually edit generated .graphql.ts files
Add a queries.graphql to a component below the query root: it declares a fragment instead
Cross-Feature Contracts
A fragment belongs to one component and never moves between features; it reaches a query only through its parent's spread. If two components need the same fields, each declares its own fragment. The full rules are in GraphQL Fragments.Next, we'll learn how fragments and data masking work.