Skip to content

fix(graphql,apollo): ensures extensions are added to resolved fields#3779

Merged
kamilmysliwiec merged 1 commit intonestjs:v14.0.0from
thiagomini:fix/fix-extensions-in-resolved-fields
Apr 23, 2026
Merged

fix(graphql,apollo): ensures extensions are added to resolved fields#3779
kamilmysliwiec merged 1 commit intonestjs:v14.0.0from
thiagomini:fix/fix-extensions-in-resolved-fields

Conversation

@thiagomini
Copy link
Copy Markdown
Contributor

@thiagomini thiagomini commented Jan 2, 2026

Closes #3778

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Issue Number: #3778

What is the new behavior?

Extensions are correctly applied to resolved fields with renamed method names.

Does this PR introduce a breaking change?

  • Yes (I think)
  • No

Other information

⚠️ Requires Attention - Possibly a breaking change

This bug only occurs when we use a custom method name for a resolve field. If we rename the method mentioned above to status, it works as expected.

However, it's worth noting that the underlying reason for this bug is how the TypeMetadataStorage class identifies a GQL field's metadata - when we rename a resolver's method name, it handles it as a new field . The fix I'll propose for this will probably fix #3524 too - but there's a catch here - this might impact how existing applications using the code-first approach build their schemas.

Currently, if we define an Object's field's metadata in both the DTO and in the @ResolveField() decorator, the @ResolveField() will take precedence (but that's a bug, as I mentioned, because of how the metadata storage thinks the method decorated with @ResolveField is actually a new field when renamed). Let's recap the example:

@ObjectType()
export class User {
  @Field(() => ID)
  id!: string;

  @Field()
  name!: string;

  @Extensions({ isPublic: true })
  @Field(() => Status, { nullable: true, description: 'DTO Description' })
  status?: Status;
}

Notice the status field description: DTO Description.

Now, let's look at the @ResolveField definition:

@ResolveField('status', undefined, {
    nullable: true,
    description: 'Resolve Field Description',
  })
  getStatus(@Parent() user: User): Status {
    return {
      id: 'status-id',
      code: 'ACTIVE',
    };
  }

Right now, when our application schema is generated, the final description will be Resolve Field Description. But if we fix the existing bug, the field description will be updated to DTO Description.

I wanted to mention this because there might be several applications right now that will have their schema definitions changed if there are any discrepancies between the DTO and the @ResolveField, so, Kamil, you probably need to decide whether to apply this patch to a new major version or a hotfix.

await app.close();
});

it('Adds extensions to resolved field', async () => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note: I've asserted that this test fails without the changed I made to the type-metadata.storage

let objectOrInterfaceTypeField =
objectOrInterfaceTypeMetadata.properties.find(
(fieldDef) => fieldDef.name === item.methodName,
(fieldDef) => fieldDef.schemaName === item.schemaName,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't know why this didn't compare schemaName to find existing fields' metadata. When comparing to methodName it wrongly assumes the methodName will always reflect the schemaName, which is not the case.

import { GraphQLSchemaBuilder } from '../lib/graphql-schema.builder';
import { ResolversExplorerService } from '../lib/services/resolvers-explorer.service';
import { ScalarsExplorerService } from '../lib/services/scalars-explorer.service';
import gql from 'graphql-tag';
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is an unused import

@kamilmysliwiec kamilmysliwiec changed the base branch from master to v14.0.0 April 23, 2026 11:17
@kamilmysliwiec kamilmysliwiec modified the milestone: 14.0.0 Apr 23, 2026
@kamilmysliwiec kamilmysliwiec merged commit c6dac18 into nestjs:v14.0.0 Apr 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants