1---2name: graphql-dev3description: Write GraphQL queries, mutations, and subscriptions — fragments, code generation, TypedDocumentNode, variables, error handling, and client setup. Use when writing GraphQL code for Saleor.4---56# GraphQL Development for Saleor78## Before writing code910**Fetch live docs**:111. Web-search `site:docs.saleor.io api-reference` for the current Saleor GraphQL schema and API reference122. Web-search `site:the-guild.dev graphql-codegen` for GraphQL Code Generator configuration and plugins133. Web-search `site:graphql.org learn` for GraphQL specification fundamentals144. Web-search `saleor GraphQL TypedDocumentNode urql` for typed client patterns155. Fetch `https://docs.saleor.io/docs/developer/api-conventions` for Saleor-specific GraphQL conventions1617## GraphQL Fundamentals1819| Operation | Purpose | Saleor Example |20|-----------|---------|----------------|21| **Query** | Read data | Fetch products, orders, categories |22| **Mutation** | Write data | Create checkout, update product, complete order |23| **Subscription** | Real-time events | Webhook subscription payloads |2425### Saleor API Characteristics2627- GraphQL is the **only** API — there are no REST endpoints28- All operations are accessed at a single `/graphql/` endpoint29- Mutations return both the result object and an `errors` array30- Queries support filtering, sorting, and cursor-based pagination31- Channel context is set via the `channel` argument or HTTP header3233## Fragments for Reuse3435| Fragment Use Case | Benefit |36|-------------------|---------|37| **Product fields** | Reuse across product list, detail, and search queries |38| **Address fields** | Share between checkout, order, and customer queries |39| **Money fields** | Consistent currency/amount selection |40| **Error fields** | Uniform error handling across mutations |41| **Image fields** | Consistent image URL and alt text selection |4243- Name fragments as `{TypeName}Fragment` (e.g., `ProductFragment`, `AddressFragment`)44- Keep fragments focused on a single concern45- Co-locate fragments with the components that use them4647## Variables and Input Types4849| Concept | Description | Example |50|---------|-------------|---------|51| **Variable** | Dynamic value passed to operation | `$id: ID!`, `$channel: String!` |52| **Input type** | Structured input for mutations | `ProductCreateInput`, `CheckoutCreateInput` |53| **Required** | Non-null variable | `$id: ID!` (with `!`) |54| **Optional** | Nullable variable | `$filter: ProductFilterInput` |55| **Default** | Variable with default value | `$first: Int = 10` |5657- Always use variables instead of string interpolation for dynamic values58- Pass channel as a variable for multi-channel operations5960## GraphQL Code Generation6162| Package | Purpose |63|---------|---------|64| `@graphql-codegen/cli` | Code generation CLI |65| `@graphql-codegen/typescript` | Generate TypeScript types from schema |66| `@graphql-codegen/typescript-operations` | Generate types for operations |67| `@graphql-codegen/typed-document-node` | Generate TypedDocumentNode objects |6869### Configuration (codegen.ts)7071| Setting | Value | Description |72|---------|-------|-------------|73| `schema` | Saleor GraphQL endpoint URL | Source of truth for types |74| `documents` | `"src/**/*.graphql"` or `"src/**/*.ts"` | Location of operations |75| `generates` | Output file paths | Where to write generated code |76| `plugins` | Array of codegen plugins | Which code to generate |7778- Run `graphql-codegen` after schema changes or when adding new operations79- Commit generated files to version control for CI consistency8081## TypedDocumentNode Pattern8283| Aspect | Benefit |84|--------|---------|85| **Query result** | Fully typed response data |86| **Variables** | Type-checked at compile time |87| **Fragments** | Types flow through fragment composition |88| **IDE support** | Autocomplete for fields and variables |89| **Refactoring** | Rename detection across operations |9091- Generated by `@graphql-codegen/typed-document-node`92- Works with urql, Apollo Client, and other GraphQL clients93- Catches schema mismatches at compile time, not runtime9495## Error Handling9697| Error Type | Location | Cause |98|-----------|----------|-------|99| **GraphQL errors** | `response.data.mutation.errors[]` | Validation, permission, business logic |100| **Network errors** | Caught by client | Connection failure, timeout, 5xx |101| **Schema errors** | Build-time (codegen) | Operation doesn't match schema |102103### Saleor Mutation Error Structure104105| Field | Type | Description |106|-------|------|-------------|107| `field` | `String` | Which input field caused the error |108| `message` | `String` | Human-readable error message |109| `code` | `Enum` | Machine-readable error code |110111- Always check `data.mutation.errors` after every mutation112- Map error codes to user-friendly messages in the storefront113114## Client Libraries115116| Client | Package | SSR Support | Caching | Best For |117|--------|---------|-------------|---------|----------|118| **urql** | `@urql/core`, `@urql/next` | Yes (via `@urql/next`) | Document cache, Graphcache | Saleor recommended |119| **Apollo Client** | `@apollo/client` | Yes | Normalized cache | Complex caching needs |120| **graphql-request** | `graphql-request` | N/A (no cache) | None | Simple scripts, server-side |121| **gql** (Python) | `gql` | N/A | None | Python scripts and tests |122123- Include `Authorization: Bearer <token>` header for authenticated operations124- Include `saleor-channel: <slug>` header for channel-scoped queries125- For SSR: ensure proper request deduplication and cache hydration126127## Operation Naming Conventions128129| Convention | Example | Benefit |130|-----------|---------|---------|131| **Prefix with verb** | `GetProducts`, `CreateCheckout` | Clear intent |132| **Suffix queries** | `ProductListQuery`, `OrderDetailQuery` | Distinguish from mutations |133| **Suffix mutations** | `CheckoutCreateMutation` | Distinguish from queries |134| **Unique names** | No duplicates across codebase | Required by codegen |135136## Pagination Patterns137138Saleor uses Relay-style cursor-based pagination:139140| Parameter | Type | Description |141|-----------|------|-------------|142| `first` | `Int` | Number of items from the start |143| `after` | `String` | Cursor after which to fetch |144| `last` | `Int` | Number of items from the end |145| `before` | `String` | Cursor before which to fetch |146147### Response Structure148149| Field | Path | Description |150|-------|------|-------------|151| **edges** | `connection.edges[]` | Array of edge objects |152| **node** | `edge.node` | The actual entity |153| **cursor** | `edge.cursor` | Opaque cursor string |154| **pageInfo.hasNextPage** | `connection.pageInfo` | More items forward |155| **totalCount** | `connection.totalCount` | Total items matching filter |156157- Use `first` + `after` for forward pagination (most common)158- Avoid using `totalCount` for large datasets (can be slow)159160## Introspection and Schema Downloading161162| Method | Tool | Use Case |163|--------|------|----------|164| **Introspection query** | Built-in GraphQL client | Development exploration |165| **GraphQL Playground** | `/graphql/` in browser | Interactive schema browser |166| **Schema download** | `graphql-codegen` | CI/CD and type generation |167| **SDL export** | `rover graph introspect <url>` | Save schema as `.graphql` file |168169## Testing GraphQL Operations170171| Test Type | Tool | What to Test |172|-----------|------|-------------|173| **Unit** | Vitest, Jest | Operation result parsing, error mapping |174| **Integration** | Saleor test instance | Full round-trip query/mutation |175| **Type check** | `tsc --noEmit` | Generated types match operations |176| **Linting** | `@graphql-eslint/eslint-plugin` | Operation naming, fragment usage |177178## Best Practices179180- Use GraphQL code generation with TypedDocumentNode for end-to-end type safety181- Define reusable fragments for common field selections182- Always use variables for dynamic values — never concatenate strings183- Check `data.mutation.errors` after every mutation call184- Name every operation uniquely with descriptive PascalCase names185- Use cursor-based pagination with `first`/`after` for list queries186- Include the `saleor-channel` header for channel-scoped operations187- Keep operations minimal — request only the fields you need188- Run `graphql-codegen` in CI to catch schema mismatches189190Fetch the GraphQL documentation for current Saleor schema, code generation setup, and client library configuration before implementing.