-
-
Notifications
You must be signed in to change notification settings - Fork 677
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
Directives support with @Directive
decorator
#369
Conversation
Hey I added some stuff to hook together our The next blocker is that defining @Query(returns => Recipe)
async __resolveReference(reference: Recipe) {
return this.items.find(item => item.title === reference.title);
} causes an error here https://github.com/JacksonKearl/type-graphql/blob/federation/src/schema/schema-generator.ts#L102 My local branch: j/type-graphql@federation...JacksonKearl:federation |
@JacksonKearl awesome! I’m on vacation so I’ll look into it later. As for the resolver function, I might make a decorator for that. I also think there’s a way to have the function be whatever name you want and define the graphql name in the options, but I’m not too sure. |
Also I believe you need to use ‘@FieldResolver()’ instead of ‘@query()’. This would go in the resolver class and have to set up the Recipe as the ‘@ObjectType(() => Recipe)’ (I’m typing on a phone and phone coding is hard) |
@j looks great! Also, the decorators should be prefixed with |
@19majkel94 yup! I actually wanted to do that but decided to just do direct to prototype. I can clean things up when I get back and also do a fully replicated demo hopefully. If I do the directives route, I could change this to @directive() instead and people can implement federation stuff that way and I’ll just remove the @federation prefix? Also, notice how @JacksonKearl did the implementation. Is that okay? What’s the best way to get typedefs and resolvers instead of an executable schema? Also, to remove federation as a dependency, should I make a pluggable printSchema option so people can bring their own? In this case, use the printSchema from the federation library? I can get these working examples next week. Let me know how you’d like them to be implemented :) |
No, we can have a generic
Current pipeline is metadata -> GraphQLSchema -> derive typeDefs and resolvers. This way we can quite easily support both worlds. We can wrap the require in try-catch, so all that users would have to do is to install the In the future there will be a plugin system that would solves this kind of tricks and complicated setup (own |
@19majkel94 great! I’ll work on this. I need to research more on what’s best to use, ‘extensionASTNodes’ or ‘astNode’. ‘extensionASTNodes’ seems like the one that should be used, but would require a chance to federations printSchema function. I haven’t really researched the proper way, since I’m not sure there really is considering there’s no direct support for directives in type constructors. |
@19majkel94 It seems that a lot of directive implementations rely on |
As discussed in #77, we have to fork the |
@19majkel94 Gotcha, in #77, in order to create directives like, @Directive('@cache(age: "7 days")') You'll need to parse that to create the proper AST, but those aren't exposed I don't think in the parser. I could parse them manually or just create a "fake" type with directives and just pull the parsed directives out, but it's just being hacky. The other option is to just do something more like, @Directive('cache', { age: '7 days' }) In the end of the day, I think the most flexible way is if |
Yes, if we need to construct AST from the decorator metadata, it should be the API we introduce.
This will break all the features that operates on We can't do that until the specification will allow for reading directives metadata and exposing them in |
cfb04bb
to
cc6f1fd
Compare
@19majkel94 I pushed a more generic @directive using the second option above. I'm doing this before my flight back home, so there's probably things I missed. Let me know what you think. Notes: I moved the auto-magic "directives" initialization of the schema so that users have to pass that part in themselves (to not be hard coded for federation support), this way you can bring in custom directives too. I think all that's needed then is to build a printer that keeps directives, which I've seen around in various issues. My previous federation tests are passing. |
@j thank you so much for taking care of this feature! I'm on the edge of my seat seeing how this PR unfolds. Just one thing, I know @19majkel94 requested these decorators to be prefixed with Here's how this would look like to me in code without the prefix and seems good enough: import { ObjectType Key, Extends } from 'typegraphql';
@Extends()
@ObjectType()
class User {
@Key()
id: number;
} Federation directives could go into its own |
Because to support #228 we might introduce the
That's correct, there will be |
In terms of conflicts, it would be possible to import { ObjectType, Key, Extends } from 'typegraphql';
import { Key as FederationKey, Extends as FederationExtends } from 'typegrapql/federation'
import { Model as PrismaModel } from 'typegraphql/prisma'
@Extends()
@ObjectType()
@FederationExtends()
@FederationKey({fields: "id"})
class User {
@Key()
id: number;
} But up to you if that's something you'd want. |
@JacksonKearl @ldiego08 You can also do the inverse of the "as" . It's sort of why I namespaced it as // decorators/index.ts
export { Federation } from './federation';
// my resolver
import { Federation } from './federation';
const { Key, Extends, ... } = Federation; But I'm not opposed to prefixing with Federation*. Another note, since they are simply directives, the @ObjectType()
@Directive('key', { fields: "id" })
class UserResolver {
// ...
} Also, this isn't supported in this PR: class User {
@FederationKey()
id: number;
} I can probably add it for easy use-cases, but if you need nested fields, I'd have to do more work to resolve child types. Also, if this repo was a mono-repo, I'd say +1 for the non-prefixed version and use I'll try to touch more things up today. |
I'm still working on this, but decided to see if the apollo team would take a PR to support passing in already constructed schemas to make this implementation a lot cleaner. apollographql/apollo-server#3013 This would make federation support really simple and not have to even be a dependency. I believe for other directives, you can use |
Update: I’m able to replicate the entire federation demo using this PR with my PR for @apollo/federation. I’ll push the examples after cleaning things up. One thing that feels awkward is defining externals on fields in a federated schema. I’ve been so used to those properties in the class to need to be filled, but noting them as readonly with undefined types makes me feel better. Another weird thing was the reference resolvers, the Apollo code samples on their demo use the world “object” for the root on all field resolvers which makes me feel that they are the same thing, but root is actually the type name with federation key. I’ll make it clear in the examples. |
I pushed examples using the PR to Next on the list is that I'm going to play around with 3rd party directives and see if I can get those to work natively with this change. |
PS, not gonna lie, this feels reaaaaaal good. 🔥 The only "gotcha" I found is that
const resolvers = {
Product: {
__resolveReference: (object, { fetchProduct }) => {
return fetchProduct(object.id);
}
}
}; and sets it as a method on the "Product" object type: console.log(ProductType.resolveReference === resolvers.Product.__resolveReference); // true I always thought that |
I came into an issue regarding my previous comment. You can't get Otherwise, @19majkel94 what are your thoughts on decorating that "resolver" with a custom signature. i.e. __resolveReference(reference, context, info)
@ObjectType(() => User)
@Directive('key', { fields: "id" })
class UserResolver {
@ObjectTypeMethod() // adds`User.resolveReference()` to `User` type
async resolveReference(reference: UserReference, { fetchUser }, info): Promise<User> {
// ...
}
} Then we can create a shortcut decorator for |
Great news!!! Congrats! |
Looks awesome, thanks a lot for the outstanding work! So if I understand correctly, I can only use this for federation with the buildFederatedSchema branch of apollo-server? |
Published as |
@pierreis I've recreated the apollo federation example from this PR using this trick but I get this error:
@j can you look at the branch |
Looks like the implementation is buggy: @Directive(`@key(fields: "upc")`) works but @Directive("key", { fields: "upc" }) not - That's why I don't like rush and "push push push to production we need that feature". Thanks god I've published it as a beta, not as stable release... |
At least right now it will be tested by others and will be much faster to fix these errors, don't worry everyone will be happy to test and give feedback and help with fixes. It's been 4 months, this PR was opened, I would not call it rush... |
This PR initially was about apollo federation and we still doesn't have a proper support from apollo-server. So we were iterating to remove federation hacks and introduce only directives support that could be use for custom federation integration. |
@pierreis @voslartomas @christopher-avila |
As far as I'm testing it, it works like a charm! I'm migrating our microservices on the typescript stack to Type-graphql, but the first integration is working perfectly. Thank you so much @MichalLytek @j and other people who worked on this. |
Hi, i´m right now confused. Does the apollo-federation implementation also enable the usage of custom directives like @intl? Sorry for disturbing, just subscribed to the PR because of the Directive support and ran into confusion regarding apollo-federation |
Not with current workaround - we use their Please ping on apollographql/apollo-server#3013 - we need this to have a real federation support without workarounds that breaks features like directives or query-complexity. |
Hi! Just found out about GraphQL directives and a Google search led me to this issue. Great work on implementing it! I'm a bit confused on the benefits of using it for, say, user authentication + authorization vs the regular way through middlewares. Also, the docs here: https://typegraphql.ml/docs/next/directives.html are helpful but the last part showing the runtime implementation seems incomplete. It'd be great to have an example of authorization at least, on how to check the actual data for the directive and doing something based off it. Thanks! |
If you implement the logic, you should just use middlewares 😉 Directives are for 3rd party integration, like Apollo Federation or Apollo Cache Control. So if you used e.g. some auth directives and while migrating to TypeGraphQL you don't want to reimplement that logic, you can just place the directive decorator and use the same approach as earlier. |
Hello, thanks a lot for implementing this. <3 |
@MichalLytek Thanks. Sorry I wasn't clear. |
@fmquaglia Soon |
I was toying with federation support and going about it the manual way.
Here's the issue I also posted on
apollo-server
apollographql/apollo-server#2965This doesn't work but if you look at the test, it shows the federated schema.
I sort of am clueless on how to support directives so it'd be cool if someone could chime in on this.
I was mocking the test case from the
federation-demo
's reviews service. Ideally we can have a fully functional replica./cc @JacksonKearl