GraphQL API merge request checklist
The GitLab GraphQL API has a fair degree of complexity so it’s important that merge requests containing GraphQL changes be reviewed by someone familiar with GraphQL.
You can ping one via the @gitlab-org/graphql-experts group in an MR or in the #f_graphql channel in Slack (available to GitLab team members only).
GraphQL queries need to be reviewed for:
- breaking changes
- authorization
- performance
Review criteria
This is not an exhaustive list.
Description with sample query
Ensure that the description includes a sample query with setup instructions. Try running the query in GraphiQL on your local GDK instance.
No breaking changes (unless after full deprecation cycle)
Check the MR for any breaking changes.
If a feature is marked as an experiment, you can make breaking changes immediately, with no deprecation period.
For more information, see deprecation and removal process.
Multiversion compatibility
Ensure that multi-version compatibility is guaranteed. This generally means frontend and backend code for the same GraphQL feature can’t be shipped in the same release.
For details, see multiple version compatibility.
Technical writing review
Changes to the generated API docs require a technical writer review.
Changelog
Public-facing changes that are not marked as an experiment require a changelog entry.
Use the framework
GraphQL is a framework with many moving parts. It’s important that the framework is followed.
- Do not manually invoke framework bits. For example, do not instantiate resolvers during execution and instead let the framework do that.
- You can subclass resolvers, as in
MyResolver.single(see deriving resolvers). - Use the
ready?method for more complex argument logic (see correct use of resolver#ready). - Use the
preparemethod for more complex argument validation (see Preprocessing).
For details, see resolver guide.
Authorization
Ensure proper authorization is followed and that authorize :some_ability is tested in the specs.
For details, see authorization guide.
Performance
Ensure:
- You have checked for N+1s and used optimizations to remove N+1s whenever possible.
- You use laziness appropriately.
Frontend GraphQL fragment changes
Apply this section when an MR changes a .graphql file under app/assets/, ee/app/assets/,
or app/graphql/queries/. Check the following for N+1 query risk.
- Trace the query depth when a fragment adds a new nested association. For example, a
checkpointsselection inside aworkflowselection inside a list ofworkflows. Follow the full query path from the root field. Confirm each level is either paginated or batch-loaded. - Watch for list-of-lists patterns. A fragment used on a list type that also fetches a sub-list is a strong N+1 signal. For example, sessions to workflows to checkpoints. Each parent record issues a separate query for its children, unless the resolver batches the loads.
- Blocker: Check for backend batch-loading on the new field. Look for
BatchLoader::GraphQLin the resolver or type. Also check whether the parent resolver includesLooksAheadwith apreloadsorunconditional_includesentry for the field. If neither is present, the MR must add batch-loading before it merges. - Blocker: Check for
QueryRecordercoverage in the matching request spec. Find the spec underspec/requests/api/graphql/oree/spec/requests/api/graphql/that mirrors the resolver path. Look for an assertion such asexpect { ... }.not_to exceed_query_limit(N)that covers the new field. Confirm the fixture creates more than one parent record, because a single record does not expose an N+1. If the assertion or the multi-record fixture is missing, the MR must add or fix the spec before it merges. - Use the performance bar or
development.loglocally to spot unexpected query counts before opening the MR.
Use appropriate types
For example:
TimeTypefor RubyTimeandDateTimeobjects.- Global IDs for
idfields
For details, see types.
Appropriate complexity
Query complexity is a way of quantifying how expensive a query is likely to be. Query complexity limits are defined as constants in the schema. When a resolver or type is expensive to call we need to ensure that the query complexity reflects that.
For details, see max complexity, field complexity and query limits.
Testing
- Resolver (unit) specs are deprecated in favor of request (integration) specs.
- Many aspects of our framework are outside the
resolvemethod and a request spec is the only way to ensure they behave properly. - Every GraphQL change MR should ideally have changes to API specs.
For details, see testing guide.