-
Notifications
You must be signed in to change notification settings - Fork 423
Extensions are not applied to resolved fields with custom method names #3778
Description
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
pnpm installpnpm 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
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.