Skip to content

Add mergeAST utility for merging multiple AST documents#4582

Open
veeceey wants to merge 2 commits intographql:16.x.xfrom
veeceey:fix/issue-1428-merge-ast-trees
Open

Add mergeAST utility for merging multiple AST documents#4582
veeceey wants to merge 2 commits intographql:16.x.xfrom
veeceey:fix/issue-1428-merge-ast-trees

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 20, 2026

Summary

  • Adds a new mergeAST utility function that merges multiple DocumentNodes into a single document by combining their selection sets
  • Fields with the same response name (alias or field name) and identical arguments are recursively merged; fields with different arguments are kept separate
  • Operations are matched by name and type (query/mutation/subscription); fragment definitions are deduplicated by name
  • Variable definitions and directives are also merged with deduplication
  • Includes 20 test cases covering: simple merging, deduplication, nested selections, arguments, aliases, named operations, variables, fragments, inline fragments, and the motivating use case from the issue

Closes #1428

Motivation

As discussed in #1428, there are many use cases where dynamically merging GraphQL AST trees is needed, particularly:

  • Resolvers that need to ensure additional fields are present in requests forwarded to backend services
  • Libraries that compose queries from multiple sources
  • React Server Components combining multiple GraphQL queries into a single request

The implementation follows the suggestion from @benjie to migrate/adapt the approach from GraphiQL's merge-ast utility, while providing a simpler, focused API that fits naturally alongside the existing concatAST utility.

API

import { mergeAST } from 'graphql';

const merged = mergeAST([documentA, documentB]);

Test plan

  • 20 unit tests covering all merging scenarios
  • Existing concatAST tests still pass
  • Verify CI passes

@veeceey veeceey requested a review from a team as a code owner February 20, 2026 08:16
@vercel
Copy link

vercel bot commented Feb 20, 2026

Someone is attempting to deploy a commit to the The GraphQL Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Feb 20, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

@yaacovCR
Copy link
Contributor

There is some feedback by @benjie on this idea from PR #4359

Implements a `mergeAST` function that merges multiple DocumentNodes by
combining selection sets of operations with matching names and types,
recursively deduplicating fields with the same response name and
arguments, and deduplicating fragment definitions.

This addresses the long-standing need (issue graphql#1428) for a way to
dynamically merge GraphQL queries, such as when resolvers need to
ensure additional fields are present in requests to backend services.

Closes graphql#1428
@veeceey veeceey force-pushed the fix/issue-1428-merge-ast-trees branch from 1697428 to a29673b Compare February 23, 2026 02:29
@veeceey
Copy link
Author

veeceey commented Feb 23, 2026

Thanks for the pointer @yaacovCR. I went through benjie's feedback on #4359 -- the DOS concern with mergeAST on unvalidated input is a fair point. I'll look into adding depth/complexity guards so it's safer to use on arbitrary documents. If the preference is still to keep this as a separate package first, I'm open to that too. Let me know how you'd like to proceed.

Adds a maxDepth option (default: 20) to mergeAST that limits recursion
depth when merging nested selection sets. This addresses the concern
raised about mergeAST being a potential denial-of-service vector when
used on unvalidated or adversarial input.

The depth is tracked through the recursive mergeSelectionSets path and
an error is thrown if the limit is exceeded. The default of 20 is high
enough for any realistic query while still protecting against malicious
nesting.
@veeceey
Copy link
Author

veeceey commented Mar 12, 2026

Went through benjie's feedback on #4359 about the DOS concern -- totally valid point. I've added a depth guard to the recursive merge path:

  • New maxDepth option on mergeAST (defaults to 20)
  • Depth is tracked through mergeSelectionSets -> mergeFieldNodes / mergeInlineFragments recursion
  • Throws a clear error if the limit is hit, with a message suggesting the option if the depth is intentional
  • Added tests covering the depth limit for both field merging and inline fragment merging

Default of 20 should be more than enough for any real-world query while still catching adversarial nesting. The option is there if someone has a legitimate deep schema and wants to raise it.

Also exported the MergeASTOptions type from the public API so consumers can use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

merge ast trees

2 participants