Prepared Docs

Prepared Docs

⌘K

    Loading navigation…

User

  1. Working With Data
  2. Graphql Development

GraphQL Development Workflow

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.

Development Setup

Running Code Generation

pnpm dev and the shell variants (pnpm dev:responder, pnpm dev:console, pnpm dev:wallboard) run codegen once, when they start. It does not watch for changes. After you add or edit a .graphql file, regenerate the types:

Then restart the TypeScript server in your IDE if it does not pick up the new .graphql.ts files.

Working with Schema Changes

Updating the Schema

For frontends participating in the AxonMundi prototype, source the schema from its composed client schema rather than a single subgraph or local API endpoint. The established Prepared911 GraphQL stack remains the canonical integration for other frontends. Refresh the prototype schema before changing operations for a newly available backend field:

pnpm schema:sync uses the self-hosted Cosmo registry by default. It requires COSMO_API_URL and either your own pnpm schema:login session or approved CI credentials. For local, unmerged AxonMundi changes, set AXONMUNDI_DIR after running just compose in that checkout.

See AxonMundi onboarding for access and credential boundaries, and Registry and schema workflow for the full refresh and drift-check process.

Generated Files

What Gets Generated

For each .graphql file, codegen creates a .graphql.ts file:

Generated File Contents

  1. Typed Document Nodes: For use with Apollo Client
  2. Query/Mutation Types: Full TypeScript types for results
  3. Variable Types: Type-safe variable interfaces
  4. Fragment Types: Reusable fragment type definitions

Git Configuration

Generated files are not committed to the repository:

This prevents:

  • Merge conflicts from generated code
  • Repository bloat
  • Outdated types if regeneration fails

Debugging GraphQL

TypeScript Type Issues

Problem: Types not updating after schema change

IntelliSense Not Working

If your IDE isn't showing proper types:

  1. Ensure codegen has run: Check for .graphql.ts files
  2. Restart TypeScript server:
    • VS Code: Cmd+Shift+P → "TypeScript: Restart TS Server"
    • Other IDEs: Check TypeScript service restart option
  3. Check imports: Ensure importing from .graphql (Vite resolves to .graphql.ts automatically)

Query Not Found

Error: Cannot find GetChatroomsDocument

Pre-commit Validation

Git hooks install with pnpm install. On commit, lint-staged runs the GraphQL checks on staged files: the layout check and graphql-eslint on .graphql files, and the Apollo oxlint rules on TypeScript. To run every check yourself:

GraphQL Lint Rules

pnpm lint:graphql is part of pnpm check, so CI runs it on every PR, uncached. It has three steps. The rules, their rationale, and what is deliberately off are in "GraphQL gate" in LINT-POLICY. Every rule is an error, and there is no suppressions file: fix the document.

1. Layout

packages/tooling-config/bin/check-graphql-layout.mjs fails when a .graphql file:

  • is outside apps/*/src, remotes/*/src, or packages/*/src (codegen would never read it, so it would get no generated types; a remote's exposes/ directory is not a document location);
  • is not named queries.graphql, mutations.graphql, subscriptions.graphql, or fragments.graphql;
  • holds a different kind of definition than its name says.

2. graphql-eslint on the documents

eslint.graphql.config.mjs checks each document against the vendored packages/data-gql/src/schema.graphql:

  • operations-recommended, including no-anonymous-operations, no-unused-variables, no-duplicate-fields, and the unique-* rules.
  • require-selections: select id where the type has one. This is what Apollo's cache normalization needs. A type without an id field is not affected.
  • no-deprecated: do not select a deprecated field. Check the schema's deprecation reason for the replacement.
  • naming-convention: PascalCase operations and fragments, camelCase variables, no Query/Mutation/Subscription/Fragment suffix, and subscriptions start with On.
  • selection-set-depth: at most 7 levels. Use fragments to flatten a deep selection. Seven fields are skipped because their shape is deep by nature: form, sections, questions (a hierarchical form), activeVersion (recursive versioning), and resource, v1, v2 (audit-log union shapes). The skip list is an escape hatch, not a fix: flatten the query with fragments first, and add a field only if its schema shape is recursive or hierarchical by design (not merely because one query is deep), saying why in the PR. Never add a field only to make a lint error pass. The skip is by field name and applies in every document, and a test pins the list.
  • known-directives: accepts Apollo's client directives (@client, @connection, @nonreactive, @unmask).

Not enabled, on purpose: no-unused-fragments (it flags a fragment read only with useFragment, which is supported, and cannot see TypeScript), no-one-place-fragments (contradicts composing a child fragment into one parent), and the schema rules (the schema is vendored).

3. Apollo usage in TypeScript

An oxlint config, oxlint.graphql.config.ts, reports: inline gql (tests and stories may build a deliberately non-generated document), explicit type arguments on Apollo hooks (useQuery<Data, Vars>(...); the generated Document infers them), indexed access on a generated result type (GetThingQuery["thing"][0]; select a named fragment instead), aliased or namespace imports of Apollo hooks, and imports of ./queries.graphql.ts instead of ./queries.graphql.

Related Documentation

  • Operations Guide - Writing queries, mutations, subscriptions
  • Fragments Guide - Using fragments

Troubleshooting Common Issues

Issue: "Cannot find module './queries.graphql'"

Cause: Codegen hasn't run yet

Solution:

Issue: "Type 'ChatroomStatus' is not assignable to type 'string'"

Cause: Using string instead of enum

Solution:

Issue: "Fragment 'UserData' not found"

Cause: Missing fragment import in GraphQL file

Solution:

Issue: Slow Codegen Performance

Cause: Processing too many files

Solutions:

  1. Ensure documents glob pattern is specific
  2. Exclude generated files from search
  3. Use more specific paths in codegen config

Mocking GraphQL for Tests

Importing Factory Functions and Document Nodes

Factory functions are exported from @prepared911/data-gql/factories, and document nodes are exported from @prepared911/data-gql:

Basic Usage

Factory functions accept an optional overrides parameter to customize specific fields:

Using with MockedProvider

The most common use case is mocking GraphQL queries in component tests. Use renderWithMockedProvider from ~src/tests instead of manually setting up MockedProvider:

Testing Custom Hooks

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 })
  • Mock structure works identically to component tests - use factories and document nodes the same way

Overriding Specific Fields

You can override any field, including nested relationships:

Working with Fragments

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:

Working with Query Results

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.

Mocking Mutations

Mutations work the same way as queries. Use factories for mutation payload types:

Mocking Subscriptions

Subscriptions can be mocked the same way as queries and mutations:

Best Practices

✅ Do

  • Use factories for all mock data: They provide type safety and consistent defaults
  • Override only necessary fields: Let factories generate defaults for fields you don't care about
  • Match query structure: Ensure mock data structure matches your GraphQL query response
  • Use renderWithMockedProvider: Use the helper from ~src/tests instead of manually setting up MockedProvider
  • Use factories for payload types: Use factories like aChatroomRequestMediaDownloadPayload for mutation/subscription payloads
  • Mock subscriptions the same way: Subscriptions work identically to queries and mutations in mocks

❌ Don't

  • Don't manually create mock objects: Use factories instead of manually typing out mock data
  • Don't ignore type errors: If TypeScript complains, your mock structure doesn't match the query
  • Don't forget __typename: The factories add this automatically, but ensure addTypename is set correctly in MockedProvider
  • Don't create overly complex mocks: Only mock the data your test actually needs
  • Don't mock GraphQL hooks directly: Remove vi.mock("@prepared911/data-gql") and use typed-document-node mocks instead

Example: Complete Test Setup

Here's a complete example showing how to use factories in a real test with multiple operations and additional providers:

Key points from this example:

  • Import document nodes from the colocated .graphql module (./queries.graphql), not from @prepared911/data-gql; enums, inputs, and factories come from @prepared911/data-gql/types and /factories
  • Use factories for payload types (e.g., aChatroomRequestMediaDownloadPayload)
  • Use expect.any(String) or expect.any(Object) for variable matching when exact values aren't needed
  • Use renderWithMockedProvider with additional providers array
  • Mock non-GraphQL hooks separately (they don't need typed-document-node mocks)

Helper Functions

The codebase includes helper functions that combine factories with MockedProvider. Import from ~src/tests:

Common additional providers:

  • WithUiCoreProviders - Provides theme, resize observer, and base providers
  • ModalContextProvider - Provides modal context
  • Custom providers as needed

The renderWithMockedProvider helper automatically:

  • Sets up MockedProvider with addTypename={false}
  • Wraps with any additional providers you pass in (such as base providers like theme, translation, etc.)
  • Allows additional providers to be passed in reverse order (outermost first)

Related Documentation

  • Operations Guide - Writing queries and mutations
  • Fragments Guide - Using fragments for type-safe data sharing
  • Apollo Client Testing - Official Apollo testing documentation

Development Tips

1. Use GraphQL Extensions

Install GraphQL extensions for your IDE:

  • VS Code: Apollo GraphQL - Provides syntax highlighting, autocomplete, and validation for GraphQL files
  • IntelliJ: GraphQL plugin

2. Apollo Client Devtools

Install browser extension for debugging:

  • View cache contents
  • Inspect active queries
  • Test mutations manually

3. Type-First Development

  1. Write your GraphQL operation first
  2. Codegen automatically generates types (or run pnpm turbo run codegen manually)
  3. Use generated types in components
  4. Let TypeScript guide your implementation

4. Fragment Organization

Keep fragments close to their consumers:

5. Debugging Network Requests

Use Apollo Client's built-in logging:

Performance Considerations

Codegen Performance

To improve codegen speed:

  1. Specific globs: Use precise file patterns
  2. Exclude tests: Don't process test files
  3. Parallel generation: Codegen runs in parallel by default

Runtime Performance

  1. Use fragments: Prevents over-fetching
  2. Avoid deeply nested queries: Keep queries shallow and focused. Excessive nesting can lead to performance issues on both the client and server. See Depth limiting

Environment-Specific Configuration

Local Development

CI Environment

Summary

This guide covered the practical aspects of working with GraphQL in this monorepo. Here's a quick reference of key concepts:

Key Workflows

  • Code Generation: Runs once when pnpm dev starts; run pnpm turbo run codegen after editing a .graphql file
  • File Organization: Use colocation to keep .graphql files next to components
  • Type Safety: Import from .graphql files (Vite resolves to .graphql.ts automatically)
  • Debugging: Use Apollo DevTools, check generated files, restart TypeScript server

Quick Reference

  • Generate types: pnpm turbo run codegen
  • Check for generated files: Look for .graphql.ts files next to .graphql files
  • Fix IntelliSense: Restart TypeScript server in your IDE
  • Troubleshoot imports: Ensure codegen has run and files exist

Related Documentation

  • Operations Guide - Writing queries, mutations, subscriptions
  • Colocation Patterns - Organizing GraphQL files
  • Using Fragments - Type-safe data sharing

Additional Resources

  • Review Apollo Client documentation for advanced patterns
  • Learn about GraphQL best practices
  • Explore Apollo's developer tools

Previous

Working with data / GraphQL Fragments

Next

Working with data / Local Storage

On this page

Development Setup
Running Code Generation
Working with Schema Changes
Updating the Schema
Generated Files
What Gets Generated
Generated File Contents
Git Configuration
Debugging GraphQL
TypeScript Type Issues
IntelliSense Not Working
Query Not Found
Pre-commit Validation
GraphQL Lint Rules
1. Layout
2. graphql-eslint on the documents
3. Apollo usage in TypeScript
Related Documentation
Troubleshooting Common Issues
Issue: "Cannot find module './queries.graphql'"
Issue: "Type 'ChatroomStatus' is not assignable to type 'string'"
Issue: "Fragment 'UserData' not found"
Issue: Slow Codegen Performance
Mocking GraphQL for Tests
Importing Factory Functions and Document Nodes
Basic Usage
Using with MockedProvider
Testing Custom Hooks
Overriding Specific Fields
Working with Fragments
Working with Query Results
Mocking Mutations
Mocking Subscriptions
Best Practices
✅ Do
❌ Don't
Example: Complete Test Setup
Helper Functions
Related Documentation
Development Tips
1. Use GraphQL Extensions
2. Apollo Client Devtools
3. Type-First Development
4. Fragment Organization
5. Debugging Network Requests
Performance Considerations
Codegen Performance
Runtime Performance
Environment-Specific Configuration
Local Development
CI Environment
Summary
Key Workflows
Quick Reference
Related Documentation
Additional Resources