graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Applying Field Metadata Outside Of `@Field()` Decorator

Open j opened this issue 4 years ago • 2 comments
trafficstars

In the GraphQL extension docs, there's an extensions middleware:

@Field({ middleware: [checkRoleMiddleware] })
@Extensions({ role: Role.ADMIN })
password: string;

As cool as this is, I'd like to add extensions & middleware field via applyDecorator to both @Field() and @ResolveField() fields.

@Field()
@Auth(Role.ADMIN)
password: string;

// and use same extension decorator with field resolvers

@ResolveField()
@Auth(Role.ADMIN)
somethingPrivate(): string {
  // ...
}

I currently use extensions & transformSchema to apply graphql-shield (since guards don't work on ResolveField). I think being able to add field middleware through a decorator would be more ideal and flexible:

export function Auth(...roles: string[]) {
  return applyDecorators(
    ApplyMiddleware(checkRoleMiddleware),
    SetMetadata('roles', roles),
    Extensions({ roles })
  );
}

There are of course alternative ways of doing this using extensions:

  1. Create a directive to visit all fields of the schema and modify their resolver if it contains the directive (essentially applying a "middleware").
  2. Use transformSchema like I did above to modify field resolvers which contain given metadata (pretty much very similar as 1st option.

The problem with these is that I can't create a module to ensure it's set up correctly. I have to create steps for users to follow to make this work correctly. I see ApplyMiddleware type of decorator being the best option unless we support registering directives and/or transformSchema functions outside of the user's GraphQL module:

@RegisterDirective()
export class AuthDirective extends SchemaDirectiveVisitor {
  // ...
}

// or

@TransformSchema()
export class AuthTransformSchema implements SchemaTransformer {
  constructor(private readonly authService: AuthService) {}

  async transform(schema: GraphQLSchema): Promise<schema> {
    // find fields with auth extensions and do some magic

    // return modified schema

    return schema;
  }
}

j avatar Mar 19 '21 23:03 j

Not sure if I'm following.

As cool as this is, I'd like to add extensions & middleware field via applyDecorator to both @Field() and @ResolveField() fields.

I think this should be doable by checking the target (whether it's a property or method) and applying the appropriate decorator.

kamilmysliwiec avatar Mar 22 '21 09:03 kamilmysliwiec

@kamilmysliwiec I mean that I want to use applyDecorator to apply both extensions and field middleware to an existing field. Right now I'd have to not use @Field() and instead create a decorator like, @AuthField(). Mainly I'm talking about the ability to apply "middleware" to an already existing @Field().

j avatar Mar 22 '21 20:03 j