Skip to content
New issue

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

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

Already on GitHub? # to your account

Feat req: allow extending ObjectType, Resolver, InputType #811

Closed
undefined-moe opened this issue Mar 10, 2021 · 4 comments
Closed

Feat req: allow extending ObjectType, Resolver, InputType #811

undefined-moe opened this issue Mar 10, 2021 · 4 comments
Labels
Community 👨‍👧 Something initiated by a community Duplicate 🔑 This issue or pull request already exists Enhancement 🆕 New feature or request

Comments

@undefined-moe
Copy link

Is your feature request related to a problem? Please describe.
I want to build a API which allows other plugin to dynamically inject some fields/resolvers.

Describe the solution you'd like

// loader.ts
import Foo from './foo';
import './bar';

/* build schema with Foo */

// foo.ts
interface Foo {
  bar: string;
}

@ObjectType()
export default class Foo {
  @Field()
  bar: string;
}

// bar.ts
import Foo from './foo'

/*
 * better works without declare module needed, 
 * but I don't think it would be easy (?)
 * it's also ok if declare is required.
 */
declare module './foo' {
  interface Foo {
    baz: number;
  }
}

@ExtendType(Foo)
class Extender {
  @Field()
  baz: number
}

and Resolver/Input could be similar to it.

Describe alternatives you've considered
I tried to parse metadata manually but as the type was parsed and stored after calling ObjectType decorator, it's hard to inject other fields(need to hook almost all decorators). Also, I think it would be awesome if type-graphql can support it.

Additional context
void

@MichalLytek
Copy link
Owner

MichalLytek commented Mar 10, 2021

Please try not to propose an API design, it's my job to develop something easy to use 😉

You should focus on describing as much as possible all the background, the details, the workarounds you do, to allow me better understand your problem and find a good solution for it.

I want to build a API which allows other plugin to dynamically inject some fields/resolvers.

Does it sounds like duplicate of #228? Or the standard GraphQL extending mechanism doesn't work in your case?

I tried to parse metadata manually but as the type was parsed and stored after calling ObjectType decorator, it's hard to inject other fields(need to hook almost all decorators).

Does it sound like a duplicate of #134? 🤔

@MichalLytek MichalLytek added Community 👨‍👧 Something initiated by a community Enhancement 🆕 New feature or request Need More Info 🤷‍♂️ Further information is requested labels Mar 10, 2021
@undefined-moe
Copy link
Author

Does it sound like a duplicate of #134?

I don't think accessing metadata is a good idea for me as I want to provide the same API of type-graphql when extending these class, creating a mock of each decorator and proxy it to the right place is really annoying.

Does it sounds like duplicate of #228?

Yes its something like that, but maybe a little different:

my program acts like this:

  • read config (from file or database)
  • require() all plugins defined in config

The problem is that the plugin is community-driven which means I don't know what will be extended and what will be exported when my code's compiling.
I'm currently using hook to allow other injection:

import { Bus } from 'main';
bus.on('foo', async (args) => {
  // do something or make some change to the args
});

Note: if designed as following, the typings should be generated with FooType, BarType will be unable to access for me.

// plugin code
import { FooType } from 'main';
class BarType extends FooType { }

#228 (comment) could be a solution for me, but it seems currently not supported.
I have two solutions for that question:

  • All extended fields must be nullable or have a default value.
  • Allow decorating the original method.
@ObjectType
class A { 
  methodA(arg1, arg2){ }
}

@ObjectType({ extend: A })
class B {
  // automatically pass the original method at the first argument
  methodA(_super, arg1, arg2){
	const value = _super();
    value.key = foo;
    return value; 
  }
}

@MichalLytek
Copy link
Owner

MichalLytek commented Mar 10, 2021

TypeGraphQL design goal is to have a statically-analysable classes which you decorate to expose properties and methods via the GraphQL API.

It was never intended to have some more complex magic, like generated args/inputs/connection based on model class, etc. So everything like hooking into metadata storage and then making declare module won't be ofically supported nor recommended in TypeGraphQL.

So if you could use the extend type keyword for your problem, then please move the discussion to #228.
Maybe try to show how the standard SDL and resolvers approach would work for that case of extending the behavior of field (resolvers), because this feature will be as close as possible to the standard graphql capabilities - it won't have any additional magic like passing the original method at the first argument.

@undefined-moe
Copy link
Author

Thanks a lot.

@MichalLytek MichalLytek added Duplicate 🔑 This issue or pull request already exists and removed Need More Info 🤷‍♂️ Further information is requested labels Mar 11, 2021
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Community 👨‍👧 Something initiated by a community Duplicate 🔑 This issue or pull request already exists Enhancement 🆕 New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants