Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a @DirectiveResolver decorator #21

Closed
anodynos opened this issue May 13, 2018 · 12 comments
Closed

Add a @DirectiveResolver decorator #21

anodynos opened this issue May 13, 2018 · 12 comments

Comments

@anodynos
Copy link

anodynos commented May 13, 2018

Currently, NestJS doesn't support GraphQL directives at all, since the directiveResolvers in makeExecutableSchema is missing https://github.com/nestjs/graphql/blob/master/lib/graphql.factory.ts#L28

I wanted to use directives in my model (for authentication etc) and I bumped into #17 but I realized it's not just the mapping to Guards, but that NestJS is totally missing directives.

PS: An example of how to use Directives https://codeburst.io/use-custom-directives-to-protect-your-graphql-apis-a78cbbe17355 and how graphql-tools implements it here https://github.com/apollographql/graphql-tools/blob/master/docs/source/directive-resolvers.md#directive-example

@anodynos
Copy link
Author

anodynos commented May 14, 2018

The more I read into graphql (I'm still a newbie), the more I think NestJs should embrace SchemaDirectives instead / also :-)

@anodynos
Copy link
Author

Any updates on this issue?

@kamilmysliwiec
Copy link
Member

New 5.0.0 has been released.

Nevertheless, we didn't ship @DirectiveResolver() decorator. GraphQLModule is compatible with directives though. The reason for this is that Apollo forces us to pass types, instead of the concrete instances of the resolvers. Therefore, directive resolvers won't be able to access the injector and internal Nest module context because we can't register them as providers.

To register directive resolver, simply pass directiveResolvers array to the GraphQLModule.forRoot() method.

@anodynos
Copy link
Author

anodynos commented Sep 13, 2018

Thanks @kamilmysliwiec - can you provide a simplistic working example?
I think it will be useful for any future reference as well.

PS: I made it work as PoC back with NestJs 4.6 & @nestjs/graphql 2.0.0 with this hack:

configure() {
   const self = this;
    // @note: we need an unbound function here, not a () =>
    this.graphQLFactory.createSchema = function(
      schemaDefintion = { typeDefs: [] },
    ) {
      return makeExecutableSchema({
        ...schemaDefintion,
        directiveResolvers: self.directiveResolvers,
        resolvers: {
          ...this.resolversExplorerService.explore(), // ignore private error, its a hack anyway!
          ...(schemaDefintion.resolvers || {}),
        },
      });
    };

    const schema = this.graphQLFactory.createSchema({ typeDefs });
   ....
}

@blissi
Copy link

blissi commented Oct 18, 2018

@kamilmysliwiec I am also interested in an example how to add directive support "the intended way".

@Tarjei400
Copy link

I made some PoC on how to use dependency injection in Directive classes, it requires one ugly hack but looks slick.
graphql.module.ts


@Module({
    imports: [
        GraphQLModule.forRootAsync({
            useFactory(factory: DirectivesFactory) {
                return {
                    typePaths: ['./**/*.graphql'],
                    debug: true,
                    playground: true,
                    installSubscriptionHandlers: true,
                    definitions: {
                        path: join(process.cwd(), 'src/graphql.schema.ts'),
                        outputAs: 'class',
                    },
                    transformSchema: (schema: GraphQLSchema) => {
                        return mergeSchemas({schemas: [schema], schemaDirectives: factory.register() });
                    }
                };
            },
            imports: [DirectivesModule],
            inject: [DirectivesFactory]
        }),

directives.module.ts

import {HttpModule, Module} from '@nestjs/common';
import {DirectivesFactory} from "./directives.factory";

@Module({
    imports: [HttpModule],
    providers: [ DirectivesFactory],
    exports: [ DirectivesFactory ]
})
export class DirectivesModule {}

directives.factory.ts

import {Inject, Injectable} from "@nestjs/common";
import {SchemaDirectiveVisitor} from "graphql-tools";
import {RestDirective} from "./rest-directive";

@Injectable()
export class DirectivesFactory {

    @Inject()
    restDirective: RestDirective;

    register() {
        return {
            rest: this.restDirective.build()
        }
    }
}

And last one rest-directive.ts

import {HttpService, Inject, Injectable} from "@nestjs/common";
import {SchemaDirectiveVisitor} from "graphql-tools";

type DirectiveArguments = {
    [name: string]: any;
}

@Injectable()
export class RestDirective {
    @Inject()
    private http: HttpService;

    async visitFieldDefinition(args: DirectiveArguments){
        const { url } = args;
        const response = await this.http.get(url).toPromise();
        return response.data;
    }

    build(): typeof SchemaDirectiveVisitor {
        const rest = this;
        class Directive extends SchemaDirectiveVisitor {
            public visitFieldDefinition(field) {
                field.resolve = async () => await rest.visitFieldDefinition(this.args);
            }
        }

        return Directive
    }
}

@Tarjei400
Copy link

Then you use can use it like this from schema:

directive @rest(url: String) on FIELD_DEFINITION

# the schema allows the following query:
type Query {
    getWeather: WeatherResponse @rest(url: "https://samples.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid=b6907d289e10d714a6e88b30761fae22")
}

@sebastian-schlecht
Copy link

Any update on the intended way to do this? We would simply need the default @Skip and @include directives.

@Tarjei400
Copy link

@sebastian-schlecht check out couple of comments above, I posted some workaround to this

@Ponjimon
Copy link

Is there any workaround how I can use directives in a code-first approach? type-graphql introduced a @Directive decorator, but I cannot use it with NestJS :(

@volbrene
Copy link

@lookapanda I have open a ticket for that problem. Hope anyone can add the new version from type-graphql:

#618

@leohxj
Copy link

leohxj commented May 28, 2020

@Ponjimon
The @directive() decorator is exported from the @nestjs/graphql package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants