Data & APIs

GraphQL Schema Reviewer

Review a GraphQL schema change for N+1 resolver risk, nullability correctness, and whether it's actually backward compatible with existing clients.

When to use this

  • A new type, field, or resolver is being added to a GraphQL schema.
  • A schema change needs a backward-compatibility check before deploying.
  • Not for REST APIs -- use api-contract-validator for schema/implementation matching on REST.

The skill file

Copy this verbatim. It's written in the SKILL.md format (YAML frontmatter + markdown instructions) that Claude Code, and increasingly other agent tools, read directly.

SKILL.md
---
name: graphql-schema-reviewer
description: Review a GraphQL schema change for backward compatibility with existing clients, resolver-level N+1 query risk, and correct nullability. Use when adding or changing types, fields, or resolvers in a GraphQL schema.
---

# GraphQL Schema Reviewer

## Backward compatibility

- **Additive is safe**: new types, new optional fields, new optional
  arguments with defaults don't break existing clients.
- **Breaking changes**: removing/renaming a field or type, changing a
  field's type, changing nullable to non-nullable (existing queries
  expecting null-safety can break), removing an enum value a client might
  send or expect, adding a new required argument.
- If a breaking change is genuinely needed, is it deprecated first
  (`@deprecated` directive with a reason) with a migration window, rather
  than removed immediately?

## Nullability correctness

- Does each field's nullability actually reflect reality? Marking a field
  non-nullable when the resolver can genuinely fail to produce it (a
  missing related record, a downstream service timeout) causes GraphQL's
  null-propagation to null out an entire parent object on that one
  field's failure -- often a bigger blast radius than intended.

## N+1 resolver risk

- Does a field resolver that fetches related data (e.g. `author` on a
  `Post` type) rely on a per-parent fetch that will run once per item in
  a list query, instead of using a batching mechanism (DataLoader or
  equivalent)? This is the single most common GraphQL performance bug and
  is easy to introduce without noticing in a single-item test.

## Output

Findings grouped: backward-compatibility, nullability, N+1 risk -- each
with the specific field/resolver and the fix.

Installing it elsewhere

The frontmatter/body split above is Claude Code's convention. Here's how to carry the same instructions into other tools:

Claude Code
.claude/skills/graphql-schema-reviewer/SKILL.md

Save the file below verbatim (frontmatter included) at that path, project-local or in ~/.claude/skills/ for a user-level skill. Claude Code loads the name/description pair to decide when to pull it in, or you invoke it directly as /graphql-schema-reviewer.

Cursor
.cursor/rules/graphql-schema-reviewer.mdc

Convert the YAML frontmatter to Cursor's rule format (description, globs, alwaysApply: false) and keep the markdown body as the rule content. Cursor surfaces it by description match, same idea as Claude Code's auto-load.

Codex CLI / Copilot
AGENTS.md

Codex CLI (and increasingly other agentic CLIs) read AGENTS.md at the repo root as always-on instructions. Paste the markdown body under a heading like ## {title}; for GitHub Copilot's coding agent, the equivalent file is .github/copilot-instructions.md.

Windsurf
.windsurfrules

Append the markdown body to .windsurfrules at the repo root. Windsurf treats the whole file as always-on context, so keep only the instructions you want applied on every request.

Where this goes wrong
  • Marking a field non-nullable without checking whether its resolver can genuinely fail to produce a value.
  • Missing an N+1 pattern because it's invisible when testing with a single parent object instead of a list.
  • Treating a nullable-to-non-nullable field change as safe when it can break existing client queries.