It is generally useful to be able to annotate fields with custom metadata for use by middleware and validators. See: #77 and #36 (my personal use case)
Since there currently isn't support for directives in GraphQL-js, an alternative might be for TypeGraphQL to add a new decorator that annotates the fiend config object passed into the GraphQLObjectType constructor with additional user-defined metadata fields.
I'd imagine the API looking something like this:
@ObjectType()
class MyObjectType {
@Metadata({ isUseful: false })
@Field()
myAnnotatedField() {
return "12"
}
}
This relatively simple change would make it possible to write middleware libraries wrapping tools such as graphql-query-complexity.
Owner note:
Read more about how to implement here: https://github.com/19majkel94/type-graphql/issues/124#issuecomment-426935908
To access a data from decorator in middleware, you can create custom decorators that uses middlewares under the hood:
function Metadata(args: { isUseful: boolean}) {
return UseMiddleware(({}, next) => {
if (!args.isUseful) {
return null; // or other logic
}
return next();
});
}
If you need to access graphql-related metadata in the middleware, you might be interested in #123.
If you think about #36 and the need to emit some data into new ObjectType constructor, it's a separate thing. I think i it may be implemented only with the plugin API, as I need to call it on schema creation - it can't be done by a middleware.
Yeah, I'm thinking this would be applied at schema creation, then a global middleware would have access to the annotated schema and would be able to work it's magic.
My suggestion here is that @Metadata would be a primitive exposed by type-graphql, which defines additional properties to be passed into a field at schema creation time.
What's the plugin API you're referring to? I can't see documentation or an open issue for it.
So you also need graphql-js related metadata like GraphQLInputObjectTypeConfig data? For #36 you only need to inject the data into the config on build types time.
Sorry but I don't understand the _"middleware would have access to the annotated schema"_ part 馃槙
If you think about #36 and the need to emit some data into new ObjectType constructor, it's a separate thing. I think i it may be implemented only with the plugin API, as I need to call it on schema creation - it can't be done by a middleware.
Yes, the proposed feature here is for an API to do this. How would you feel about integrating this feature in the core since there isn't a plugin API yet?
Sorry but I don't understand the "middleware would have access to the annotated schema" part :confused:
It _would_ have access to it via the info.schema property. But having checked the documentation, you're right to be confused by me talking about middleware in relation to #36 (for some reason I thought global middlewares were applied once to the whole query, rather than on every resolver :confused:).
This would be used top-level validation rule, possibly applied by the graphql server before the schema is even evaluated.
How would you feel about integrating this feature in the core since there isn't a plugin API yet?
For #36 I would have to add a field to decorator config, store it in MedatataStorage and manually place in ObjectType config object on schema generation.
But I don't like this tight coupling, I like the idea of microservices and I would prefer to go with multi packages monorepo. To do this, I need to develop a plugin API, so @typegraphql/query-complexity would be installed by user, registered in buildSchema plugins array and extend the @Field decorator with complexity settings.
So basically with plugin API you could be able to do the integration by yourself, e.g. for other graphql package not listed in roadmap here.
for some reason I thought global middlewares were applied once to the whole query, rather than on every resolver 馃槙
They are global global 馃槅 But basically, scoping type of global middlewares makes sense 馃檮
I need to think how to declare the middleware type resolverOnly|fieldOnly|all.
For classes it might be a static prop:
export class GlobalQueryMiddleware implements MiddlewareInterface<Context> {
static type = "resolverOnly";
async use({ context, info }: ResolverData<Context>, next: NextFn) {
return next();
}
}
But for middleware functions it would have to be like the defaultProps for react function components:
export const GlobalQueryMiddleware: MiddlewareFn = async ({ info }, next) => {
await next();
};
GlobalQueryMiddleware.type = "resolverOnly";
For now nobody complained about the global middlewares scoping, seems I should make it more flexible in the future 馃槈
But I don't like this tight coupling, I like the idea of microservices and I would prefer to go with multi packages monorepo. To do this, I need to develop a plugin API, so @typegraphql/query-complexity would be installed by user, registered in buildSchema plugins array and extend the @Field decorator with complexity settings.
I absolutely agree with this, a plugin system is definitely the right way to go. But it's a big problem exposing a public GraphQL API in production without proper query complexity validation as it basically makes it trivial to DOS your server by crafting a malicious query.
So I'm wondering if a nice compromise between those two concerns would be for an API that:
1) Allows TypeGraphQL to be used in production now (for me, it's the only blocker -- everything else is there)
2) Is mostly forwards compatible with a future migration to a plugin-based architecture. Users can keep their declarative complexity annotations, then just change the decorator factory to call into the plugin API when the migration to a plugin architecture happens.
They are global global :laughing: But basically, scoping type of global middlewares makes sense roll_eyes
That would be nice, but at least for #36, probably isn't required as I can just validate the query before evaluating it against the schema.
Also, worth mentioning.... I'd be happy to have a go at implementing it.... assuming you were ok with the general approach :slightly_smiling_face:
Sure, it's quite easy do implement it:
@Field, @Query, etc. decorators options interfaceObjectType and others constructorsgraphql-js typings to make TS not to complain about graphql-query-complexity related fieldsAnd remember to create a test cases for this feature and add info about it in docs.
Please stick to the commit message guidelines that I use (see commit history).
In the future I would extract it to the separate plugin package but the API will be the same. Users only need to install the package and register the plugin to make it works again.
Awesome. Thanks for the pointers, I'll take a look over the next few days.
@chrisdevereux
Is it possible to add custom metadata now? We're using join monster, and that requires us to add additional metadata to GraphQL objects.
@wesselvdv
Not yet but I think it's easy to implement even by beginners 馃槈
API usage - @Metadata:
@ObjectType()
class MyObjectType {
@Metadata({ isUseful: false })
@Field()
myAnnotatedField() {
return "12"
}
}
Steps to do:
Metadata to place the metadata object in storagefieldsMap[field.schemaName] = {
// ...
deprecationReason: field.deprecationReason,
...(field.metadata || {}),
};
Currently this is blocked by https://github.com/graphql/graphql-js/issues/1527#issuecomment-457828990. The GraphQL team will remove the current hackish workaround and introduce a special property in type config for placing custom metadata, so we have to wait for 14.2.0 release 馃槈
@19majkel94 graphql/graphql-js#1527 has been implemented
We should rename this decorator to @Extensions.
@MichalLytek I would like to give this a go
Already taken by my friend @VeryVicious, will let you know @palaparthi if something changes.
Is @VeryVicious still working on this?
@glen-84 I'm afraid not, you can take try to tackle it down if @palaparthi is not interested anymore 馃槈
Hello 馃憢
We are also very interested in this feature for our project 馃檪If neither of them are interested anymore I can give it a try as well 馃檪
@hihuz,
Maybe extensions should be read-only (Readonly<Record<string, any>>)? I think it is in graphql-js.
Thank you for the suggestion @glen-84 馃檪I agree, and have updated my branch to reflect it.
I have now opened a PR for review and I hope we can make some progress on it.
Closed by #521 馃敀
Released in v0.18.0-beta.10 馃帀
Most helpful comment
Closed by #521 馃敀
Released in
v0.18.0-beta.10馃帀