Skip to content

Conversation

@HunterLarco
Copy link
Contributor

@HunterLarco HunterLarco commented Aug 30, 2025

Description

This PR enhances the developer experience when debugging GraphQL schema import errors by transforming basic error messages into proper GraphQLError instances with accurate source locations. For example:

Missing type output before this PR

throw new Error(`Couldn't find type ${dependencyName} in any of the schemas.`);
Error: Couldn't find type Post in any of the schemas.
    at syntaxError (node_modules/graphql/error/syntaxError.js:15:10)

Missing type output after this PR

Couldn't find type Post in any of the schemas.

/path/to/schema.graphql:2:9
1 | type Dummy {
2 |   test: Post
  |         ^
3 | }

This significantly improves the developer experience by immediately pinpointing where missing type references occur, reducing debugging time and making schema issues much easier to resolve.

Fixes#7443

Risks

This PR just changes the error format and leaves all business logic intact so I expect it to have low risk. However, it does enable parsing graphql with locations. This will require more memory (though I expect negligible) and could result in graphql source code to be exposed through error messages. That said, a graphql server with schema reflection already exposes the graphql source code and any graphql queries on the client also expose the schema. So while there is risk of exposing raw source code, it's only schema which is often already exposed. If this is a concern we could consider plumbing an option to remove locations ( with the trade off of worsening debug messages).

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can
reproduce. Please also list any relevant details for your test configuration

  • Updated existing type-not-found tests to include checks for GraphQLError responses
  • Added a new test to include cases where within a definition, multiple identical types are not found as this case is handled GraphQLError in a way which wasn't previously (shows debug output for each reference).

To run: npm test

Test Environment:

  • OS: OSX
  • @graphql-tools/...: import
  • NodeJS: v20.18.0

Checklist:

  • I have followed the
    CONTRIBUTING doc and the
    style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests and linter rules pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@changeset-bot
Copy link

changeset-bot bot commented Aug 30, 2025

🦋 Changeset detected

Latest commit: b04b242

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@graphql-tools/import Patch
@graphql-tools/graphql-file-loader Patch
@graphql-tools/node-require Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 30, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Schema import errors now include precise source locations and context, surfaced as GraphQLError. Multiple occurrences in a file are reported with all positions.
  • Tests
    • Added assertions for detailed error formatting and multi-location reporting. Standardized error creation via a helper.
  • Chores
    • Added changeset for a patch release describing enhanced error debugging.

Summary by CodeRabbit

  • New Features

    • Location-aware dependency tracking and diagnostics.
    • Richer GraphQL errors with precise file/line highlights, including multiple locations for repeated missing types.
  • Refactor

    • Dependency tracking reworked to support location-aware data.
    • Public dependency-extraction API now returns a location-aware map structure (breaking change).
  • Tests

    • Expanded tests to assert GraphQLError type and exact formatted messages with locations.
    • Added fixture schema to validate multiple missing-type occurrences.

Walkthrough

Refactors dependency tracking to map dependency names to NameNode references, updates extractDependencies return type, enables location-aware parsing, and switches error handling to GraphQLError with precise locations. Tests now assert GraphQLError and exact formatted messages; a new SDL fixture exercises multiple missing-type occurrences.

Changes

Cohort / File(s) Summary
Core dependency tracking refactor
packages/import/src/index.ts
Replaces Set<string> with DependencySet = Map<string, Set<NameNode>> and DependenciesByDefinitionName; adds addToDependencySet; updates AST visitors to collect NameNode references; iterates dependency maps via keys/entries; enables location-aware parsing; throws GraphQLError using NameNode locations; changes extractDependencies return type.
Tests: assertions & behavior
packages/import/tests/schema/import-schema.spec.ts, packages/utils/tests/mergeIncrementalResult.spec.ts
Tests updated to expect GraphQLError (imported from graphql) and to assert exact toString() diagnostics; replaced direct GraphQLError instantiation in utils tests with createGraphQLError helper.
Test fixtures
packages/import/tests/schema/fixtures/type-not-found/h.graphql
Adds type Fixture { first: Foo, second: Foo, third: Foo } to validate multiple unresolved references.
Release metadata
.changeset/stupid-webs-stand.md
Adds a changeset declaring a patch for @graphql-tools/import describing improved error debugging with source locations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant Importer as import/index.extractDependencies
  participant Parser as parseGraphQLSDL
  participant Visitor as AST Visitor
  participant Resolver as Dependency Resolver

  Caller->>Importer: extractDependencies(filePath, fileContents)
  Importer->>Parser: parseGraphQLSDL(fileContents) (with locations enabled)
  Parser-->>Importer: AST
  Importer->>Visitor: visit(AST)
  Visitor-->>Importer: dependenciesByDefinitionName (Map<defName, DependencySet>)
  Importer->>Resolver: resolve dependencies (uses NameNode locations)
  alt unresolved dependency found
    Resolver-->>Importer: unresolved name + Set(NameNode)
    Importer-->>Caller: throw GraphQLError(locations from NameNodes)
  else all dependencies resolved
    Importer-->>Caller: return { definitionsByName, dependenciesByDefinitionName }
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

A rabbit maps names to nodes with care,
Tiny paws tracing markers everywhere.
When Foos go missing, lights now show the line,
Multiple spots found — the error's fine.
I hop, I cheer, the trace is clear. 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 52ba07b and b04b242.

📒 Files selected for processing (3)
  • .changeset/stupid-webs-stand.md (1 hunks)
  • packages/import/src/index.ts (20 hunks)
  • packages/utils/tests/mergeIncrementalResult.spec.ts (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@HunterLarco HunterLarco marked this pull request as ready for review September 3, 2025 11:39
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/import/src/index.ts (1)

345-365: Bump major version and document breaking API change

  • The return type of extractDependencies has changed from Map<string, Set<string>> to DependenciesByDefinitionName (Map<string, Map<string, Set<NameNode>>>), which breaks existing type contracts.
  • In packages/import/package.json, bump the MAJOR version and add a BREAKING CHANGE entry to CHANGELOG.md describing this signature change.
🧹 Nitpick comments (2)
packages/import/tests/schema/import-schema.spec.ts (1)

803-816: Consider using a more robust path comparison approach.

The hardcoded path.resolve(__dirname, './fixtures/type-not-found/a.graphql') in the expected error string may cause issues when tests run in different environments or with different path separators.

Consider normalizing paths or using a more flexible assertion:

-      expect(e.toString()).toBe(
-        `
-Couldn't find type Post in any of the schemas.
-
-${path.resolve(__dirname, './fixtures/type-not-found/a.graphql')}:2:9
-1 | type Dummy {
-2 |   test: Post
-  |         ^
-3 | }
-`.trim(),
-      );
+      const expectedPath = path.resolve(__dirname, './fixtures/type-not-found/a.graphql');
+      expect(e.toString()).toContain('Couldn\'t find type Post in any of the schemas.');
+      expect(e.toString()).toContain(expectedPath);
+      expect(e.toString()).toContain('2 |   test: Post');
+      expect(e.toString()).toContain('  |         ^');
packages/import/src/index.ts (1)

238-264: Consider optimizing the dependency collection logic.

The new dependency collection creates a fresh Map for each field, which could be optimized by reusing the dependency set when possible.

Consider this optimization to reduce object allocations:

-            const newDependencySet = new Map<string, Set<NameNode>>();
+            let newDependencySet = dependenciesByDefinitionName.get(fieldDefinitionName);
+            if (!newDependencySet) {
+              newDependencySet = new Map<string, Set<NameNode>>();
+            }
             switch (field.kind) {
               case Kind.FIELD_DEFINITION:
                 visitFieldDefinitionNode(field, newDependencySet, dependenciesByDefinitionName);
                 break;
               case Kind.INPUT_VALUE_DEFINITION:
                 visitInputValueDefinitionNode(
                   field,
                   newDependencySet,
                   dependenciesByDefinitionName,
                 );
                 break;
             }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 07124d5 and 52ba07b.

📒 Files selected for processing (3)
  • packages/import/src/index.ts (19 hunks)
  • packages/import/tests/schema/fixtures/type-not-found/h.graphql (1 hunks)
  • packages/import/tests/schema/import-schema.spec.ts (8 hunks)
🔇 Additional comments (11)
packages/import/tests/schema/fixtures/type-not-found/h.graphql (1)

1-5: LGTM! Test fixture appropriately exercises multiple missing type references.

This fixture effectively supports the new test case for validating multiple error locations when a type is missing multiple times within a single definition.

packages/import/tests/schema/import-schema.spec.ts (3)

4-4: LGTM! GraphQLError import supports enhanced error handling.

The addition of GraphQLError import aligns with the refactored error handling that provides location-aware error reporting.


824-837: LGTM! Error assertions properly validate GraphQLError instances.

The test correctly verifies both the error type and the detailed error message with location information.


943-974: Excellent test coverage for multiple missing type references!

This new test case effectively validates that all occurrences of a missing type are reported with their respective locations, which greatly improves debugging experience.

packages/import/src/index.ts (7)

14-14: LGTM! Essential imports for location-aware error handling.

The addition of GraphQLError and NameNode imports properly supports the enhanced error reporting with source locations.

Also applies to: 24-24


321-343: Well-documented type definitions for dependency tracking!

The new DependencySet and DependenciesByDefinitionName types with clear documentation improve code maintainability and make the location-aware dependency tracking implementation easier to understand.


355-355: Location-aware parsing enabled as intended.

Removing noLocation: true from parseGraphQLSDL enables location data in AST nodes, which is essential for the enhanced error reporting feature.


284-294: Excellent error reporting with precise location information!

The GraphQLError with location nodes provides exactly the debugging improvement described in the PR objectives. This will significantly help developers identify where missing type references occur.


761-769: Well-implemented helper function for dependency tracking.

The addToDependencySet helper properly manages the Map of Sets structure and ensures efficient addition of NameNode references.


823-841: Ignore the interface dependency logic concern: both node.name and namedTypeNode.name yield NameNode objects, so allDependencies remains type-consistent and no mixing with raw strings occurs.

Likely an incorrect or invalid review comment.


553-553: Verify missing dependency handling when iterating dependency keys

Line 553 in packages/import/src/index.ts: the loop now iterates solely over dependenciesOfDefinition.keys(). No tests cover this path—please confirm definitions without dependencies are still handled as before.

!definitionsByName.has(dependencyName)
) {
throw new Error(`Couldn't find type ${dependencyName} in any of the schemas.`);
throw new GraphQLError(
Copy link
Owner

@ardatan ardatan Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use createGraphQLError helper from @graphql-tools/utils to fix the problems between GraphQLjs versions.

@ardatan ardatan merged commit 211ef44 into ardatan:master Sep 5, 2025
5 of 14 checks passed
ardatan added a commit that referenced this pull request Sep 22, 2025
…7437)

* plumb nodes

* add locations

* self review

* update tests

* one missing type multiple times in a single type

* self review

* Use createGraphQLError

---------

Co-authored-by: Arda TANRIKULU <ardatanrikulu@gmail.com>
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.

2 participants