Skip to content

Extensions are not applied to resolved fields with custom method names #3778

@thiagomini

Description

@thiagomini

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

Say that we defined a DTO like this:

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

  @Field()
  name: string;

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

And we have the UserResolver defined like this:

@Resolver(() => User)
export class UserResolver {

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

When the GQL Host Schema is generated, the User.status field does not have any extensions.

Minimum reproduction code

https://github.com/thiagomini/input-field-no-extension-bug/tree/bug-with-resolve-field

Steps to reproduce

  1. pnpm install
  2. pnpm run start:extensions

You should see the assertion failing:

AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal:

[Object: null prototype] {
  complexity: undefined
}

should loosely deep-equal

{
  complexity: undefined,
  isPublic: true
}
    at bootstrap (/home/thiago/Development/roomvy/repros/input-field-no-extension-bug/src/print-extensions.ts:25:10) {
  generatedMessage: true,
  code: 'ERR_ASSERTION',
  actual: [Object: null prototype] { complexity: undefined },
  expected: { complexity: undefined, isPublic: true },
  operator: 'deepEqual',
  diff: 'simple'
}

Expected behavior

The User.status field should have the isPublic extension key.

Package version

13.2.3

Graphql version

graphql: 16.12.0
apollo/server: 5.2.0

NestJS version

11.1.9

Node.js version

24.11.1

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

⚠️ 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 abovementioned 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 recape 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions