Working with code generation, debugging, and troubleshooting
This guide covers the day-to-day development workflow when working with GraphQL, including code generation, debugging, and troubleshooting common issues.
Codegen runs automatically when you start development:
When you save a .graphql file, codegen automatically:
.graphql.ts fileFor manual generation (rarely needed):
When the backend GraphQL schema changes:
turbo run dev, the schema updates automaticallyturbo run codegen to pull latest schemaThe schema is fetched from:
http://localhost:3000/graphql (This will be changed to not need the API running locally soon)To manually introspect and save the schema:
This updates:
src/generated/schema.graphql - Human-readable schemasrc/generated/graphql.introspection.json - For toolingFor each .graphql file, codegen creates a .graphql.ts file:
Generated files are not committed to the repository:
This prevents:
Problem: Types not updating after schema change
If your IDE isn't showing proper types:
.graphql.ts files.graphql (Vite resolves to .graphql.ts automatically)Error: Cannot find GetChatroomsDocument
The easiest way to catch issues before committing is to enable pre-commit hooks:
This configures git to automatically run linting and formatting checks before each commit. The hooks will run the necessary validation commands for you.
If you prefer to run checks manually, or if hooks aren't enabled:
Our GraphQL ESLint configuration helps maintain code quality by enforcing best practices and catching potential issues early. The rules are configured in turbo/packages/toolchain/eslint.config.base.mjs and apply to all .graphql files.
operations-recommended preset from @graphql-eslint/eslint-plugin - Includes essential rules:
no-anonymous-operations - Ensures all operations are named (better for debugging and logging)known-type-names - Validates that all referenced types exist in the schemaknown-argument-names - Ensures all arguments are defined by their fieldsknown-fragment-names - Validates fragment referenceslone-anonymous-operation - Prevents mixing anonymous and named operations@graphql-eslint/no-unused-variables - Error if you declare variables but don't use themWhat it does: Limits the depth of nested field selections to prevent overly complex queries that can impact performance.
When to disable: Legitimate deep queries (e.g., audit logs, detailed reports, complex nested data structures).
How to fix:
Example:
What it does: Ensures fragments and operations have an id field selected if available. This is required for Apollo Client caching to work properly.
When to disable: Id isn't the index field for the object.
How to fix:
Example:
What it does: Warns when using deprecated fields or types that are scheduled for removal.
When to disable: Legacy code using deprecated fields that need migration to newer alternatives.
How to fix:
Example:
What it does: Prevents selecting the same field multiple times in a selection set.
How to fix: Remove duplicate field selections - this is usually a simple fix.
Example:
What it does: Enforces consistent naming conventions for operations, types, and fields.
How to fix: Rename operations/types to match our conventions:
Get or describe the data (e.g., GetUser, SearchIncidents)UpdateChatroom, CreateIncident)On (e.g., OnChatroomUpdate, OnMessageReceived)Example:
When working in files with suppressions:
This automatically removes suppressions for violations that have been fixed.
✅ Do:
❌ Don't:
Check for GraphQL ESLint violations:
Run linting for affected packages only:
Prune obsolete suppressions:
turbo/packages/toolchain/eslint.config.base.mjsturbo/apps/dispatch/eslint-suppressions.jsonCause: Codegen hasn't run yet
Solution:
Cause: Using string instead of enum
Solution:
Cause: Missing fragment import in GraphQL file
Solution:
Cause: Processing too many files
Solutions:
documents glob pattern is specificFactory functions are exported from @prepared911/data-gql/factories, and document nodes are exported from @prepared911/data-gql:
Factory functions accept an optional overrides parameter to customize specific fields:
The most common use case is mocking GraphQL queries in component tests. Use renderWithMockedProvider from ~src/tests instead of manually setting up MockedProvider:
For testing custom hooks that use Apollo Client hooks (useQuery, useMutation, useSubscription), use renderHook from @testing-library/react with WithMockedProvider as the wrapper:
Key points for hook testing:
WithMockedProvider is a function that returns a component wrapper: wrapper: WithMockedProvider({ mocks, addTypename: false })You can override any field, including nested relationships:
Factory functions work seamlessly with fragment types. When you use a fragment in a query, you can create mock data that matches the fragment structure:
When mocking query results, structure your mock data to match the query's return type:
Note: The test setup uses a fixed Faker seed (faker.seed(0)), which means factory-generated mock data will be consistent across test runs. When you don't override specific fields, the factories will generate the same random values every time, making your tests more predictable and easier to debug.
Mutations work the same way as queries. Use factories for mutation payload types:
Subscriptions can be mocked the same way as queries and mutations:
renderWithMockedProvider: Use the helper from ~src/tests instead of manually setting up MockedProvideraChatroomRequestMediaDownloadPayload for mutation/subscription payloads__typename: The factories add this automatically, but ensure addTypename is set correctly in MockedProvidervi.mock("@prepared911/data-gql") and use typed-document-node mocks insteadHere's a complete example showing how to use factories in a real test with multiple operations and additional providers:
Key points from this example:
@prepared911/data-gql (not from .graphql files)aChatroomRequestMediaDownloadPayload)expect.any(String) or expect.any(Object) for variable matching when exact values aren't neededrenderWithMockedProvider with additional providers arrayThe codebase includes helper functions that combine factories with MockedProvider. Import from ~src/tests:
Common additional providers:
WithUiCoreProviders - Provides theme, resize observer, and base providersModalContextProvider - Provides modal contextThe renderWithMockedProvider helper automatically:
MockedProvider with addTypename={false}Install GraphQL extensions for your IDE:
Install browser extension for debugging:
turbo run codegen manually)Keep fragments close to their consumers:
Use Apollo Client's built-in logging:
To improve codegen speed:
This guide covered the practical aspects of working with GraphQL in the Prepared monorepo. Here's a quick reference of key concepts:
turbo run dev, or manually with turbo run codegen.graphql files next to components.graphql files (Vite resolves to .graphql.ts automatically)turbo run codegen (usually automatic during turbo run dev).graphql.ts files next to .graphql filesOn this page