Introduction to GraphQL usage in the axon911-frontend monorepo
The axon911-frontend monorepo uses a unified GraphQL code generation approach to ensure type safety, maintainability, and developer productivity across all frontend applications.
Architecture Overview
Our GraphQL implementation follows these key principles:
Unified Code Generation: Single configuration in @prepared911/data-gql package
Type Safety: Full TypeScript types generated from the GraphQL schema
Colocation: GraphQL documents live alongside the components that use them
Data Masking: Each component declares its own fragment and reads only those fields (dataMasking: true and masked codegen types)
Lint Enforcement: pnpm lint:graphql (part of pnpm check) checks document layout and, with graphql-eslint, each document against the schema; oxlint rules cover how TypeScript uses Apollo
Prerequisites
Before diving into GraphQL in this monorepo, you should be familiar with:
TypeScript: Interfaces, types, generics, and type inference
React: Component patterns and hooks (especially useQuery, useMutation)
Apollo Client: Basic understanding of GraphQL client libraries
GraphQL: Query syntax, fields, variables, and basic operation types
If you're new to any of these, consider reviewing the relevant documentation first.
Documentation Structure
This GraphQL documentation is organized to follow a natural learning progression:
Operations (this section): Learn to write queries, mutations, and subscriptions
Colocation: Understand where to organize GraphQL files with your components
Fragments: Learn component-owned fragments, data masking, and query roots
Development: Master the day-to-day workflow, debugging, and troubleshooting
Each section builds on the previous one, so we recommend reading them in order.
Key Concepts
Schema-First Development
The GraphQL schema serves as the single source of truth for both backend and frontend. Types, enums, and interfaces are all generated from this schema, ensuring consistency across the stack.
Near-Operation-File Generation
Using the near-operation-file preset, generated types are placed in the same folder as your .graphql files:
A document file is named queries.graphql, mutations.graphql, subscriptions.graphql, or fragments.graphql, holds only that kind of definition, and lives under src/ of an app, remote, or package. pnpm lint:graphql fails otherwise, including for a file in a remote's exposes/ directory, which codegen never reads.
Typed Document Nodes
Instead of auto-generated hooks, we use typed document nodes with Apollo Client's useQuery, useMutation, and useSubscription hooks. This provides better flexibility and type safety.
Quick Start
1. Write Your GraphQL Operation
Create a queries.graphql file next to your component:
2. Run Code Generation
pnpm dev (and the shell variants) run codegen once, when they start. It does not watch: after you add or edit a .graphql file, regenerate the types yourself:
3. Use in Your Component
These rules help maintain clean, efficient GraphQL operations and prevent over-fetching.
The TypeScript compiler catches a wrong variable type, such as limit: "20". It also catches a magic string for an enum, because generated enums are native TypeScript enums: status: "ACTIVE" fails to compile. Comparisons are the exception: status === "ACTIVE" still compiles, so pnpm lint:graphql rejects it (typescript/no-unsafe-enum-comparison). Compare with the generated member (ChatroomStatus.Active).
Import Guidelines
Understanding where to import GraphQL-related types and values is crucial for maintaining proper code organization and leveraging the benefits of colocation. Our setup uses two primary import sources, each serving a specific purpose.
Import from @prepared911/data-gql
The centralized @prepared911/data-gql package contains shared, reusable types that are used across multiple components and operations:
Enums - Type-safe constants for GraphQL enum values
Input types - Types for mutation and query variables
Scalar types - Custom scalar types defined in the GraphQL schema
Shared type system types - GraphQL infrastructure types that are reused across operations
Factory functions (for testing) - Test data factories
These types are centralized because they represent shared concepts that don't belong to any single component or operation.
Import from Colocated .graphql Files
Component-level GraphQL files (colocated with your components) export operation-specific types and values:
Document nodes - Typed document nodes for use with Apollo hooks
Operation result types - Query, Mutation, and Subscription return types
pnpm lint:graphql runs as part of pnpm check. See "GraphQL gate" in LINT-POLICY for the full rule list. In short: documents are in the right place with the right name; every operation is named and validated against the schema (no unused variables, no deprecated fields, id selected where the type has one); and TypeScript does not use inline gql, explicit hook type arguments, or indexed access on generated result types. There is no suppressions file: fix the document.