-
Notifications
You must be signed in to change notification settings - Fork 398
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
@nestjs/graphql redundancy required for types #85
Comments
I'm looking exactly into the same issue. Though https://docs.nestjs.com/graphql/resolvers-map describes TS being generated out of GraphQL schema, but when one wants to add decorators to those typings, they become unmaintanable anymore. This is how I perceive that, at least. GraphQL files being less advanced than TS files should be generated out of these, not vice versa. I try Nest.js, wanted to check out GraphQL, but I came across that issue, and I'm not moving forward with GraphQL until it is resolved in some way. That kind of redundancy is one of the worst. |
@radarsu What would this .dto.ts file exactly be? Wouldn't the graphql input type for the entity replace this supposed dto? At least that's how I'm doing it.
I think that the problem is that graphql and entity represent different logical things. One thing is how you store internal data and the other is which queries and actions (mutations) you offer as an api.
The |
This is hard topic, because for now if you are building an API you do layers:
adding fifth layer (GraphQL schemas) makes it even harder to develop. |
My current solution are custom decorators. I mixed decorators from Nest and Type-graphql. export const combineDecorators = (decorator1: any, decorator2: any) => {
return (options1: any[] = [], options2: any[] = []): any => {
let fn1;
let fn2;
try {
fn1 = decorator1(...options1);
} catch (e) {
}
try {
fn2 = decorator2(...options2);
} catch (e) {
}
return (target: any, key: string, index: any) => {
if (typeof fn1 === 'function') {
fn1(target, key, index);
} else if (typeof decorator1 === 'function') {
decorator1(target, key, index);
}
if (typeof fn2 === 'function') {
fn2(target, key, index);
} else if (typeof decorator2 === 'function') {
decorator2(target, key, index);
}
};
};
};
// type-grapqhl + @nest/graphql - type-graphql uses options more often so its first
// class decorators
export const RadarsuResolver = combineDecorators(TypeResolver, NestResolver);
// method decorators
export const RadarsuMutation = combineDecorators(TypeMutation, NestMutation);
export const RadarsuQuery = combineDecorators(TypeQuery, NestQuery);
export const RadarsuSubscription = combineDecorators(TypeSubscription, NestSubscription);
// args decorators
// export const RadarsuArgs = combineDecorators(TypeArg, NestArgs);
export const RadarsuArgs = (name?: string, options1: any[] = [], options2: any[] = []): any => {
const fn1 = TypeArg(name, ...options1);
const fn2 = NestArgs(name, ...options2);
return (target: any, key: string, index: any) => {
if (typeof fn1 === 'function') {
fn1(target, key, index);
}
if (typeof fn2 === 'function') {
fn2(target, key, index);
}
};
}; Then in resolvers it looks like that: // requests allowed with token
@UseGuards(TokenGuard)
@RadarsuResolver()
export class AuthResolver {
public constructor(
private readonly authService: AuthService,
) { }
// queries
// sending confirmation response
@RadarsuQuery([() => Boolean, { nullable: true }])
public async response(@RadarsuArgs('id') id: number, @NestContext() ctx): Promise<void> {
if (_.isFunction(config.idAwaiter[id])) {
config.idAwaiter[id]();
delete config.idAwaiter[id];
}
}
// mutations
// login
@RadarsuMutation([() => Boolean, { nullable: true }])
public async loginUser(@RadarsuArgs('input') input: LoginUserInput): Promise<void> {
const userLogged = await this.authService.login(input);
config.pubSub.publish('userLogged', { userLogged });
}
// sign up
@RadarsuMutation([() => Boolean, { nullable: true }])
public async createUser(@RadarsuArgs('input') input: CreateUserInput): Promise<void> {
const userCreated = await this.authService.create(input);
config.pubSub.publish('userCreated', { userCreated });
}
} And in app.module: GraphQLModule.forRootAsync({
async useFactory() {
config.graphql.schema = await buildSchema({
resolvers: [AuthResolver, PublicResolver],
emitSchemaFile: path.resolve(process.cwd(), `data/graphql/schema.graphql`),
});
return config.graphql;
},
}), |
Perfect @radarsu. This is exactly what I was thinking about in terms of Nest and TypeGraphQL integration cc @19majkel94 |
Combined decorators are an obvious solution for the basic integration. The trickiest part for me is making guards, interceptors and pipes working (see #135 (comment)), as I haven't dug into the nest execution pipeline yet. For now I have to fix the core before 1.0 release, so after that we can think about deeper integration @kamilmysliwiec 😉 |
@19majkel94 solution presented by @radarsu is using powerful TypeGraphQL capabilities of generating schema directly from metadata provided by generators, and at the same time, is leaving the rest of responsibilities to Nest itself (execution pipeline + framework's specific tooling [guards, interceptors, etc]). Basically, Nest already provides an integration with So generally, I could integrate these two at |
TypeGraphQL is not only about typeDefs from classes but also a set of useful features for creating the API. Some of them have equivalents in Nest (dependency injection, Without controlling the execution of resolvers, a lot of features that are coming up next won't be usable in Nest (MichalLytek/type-graphql#193, MichalLytek/type-graphql#142, MichalLytek/type-graphql#51, MichalLytek/type-graphql#44), so supporting only subset of features shouldn't be the final goal.
Everything is possible 😄 It's just a matter of time and effort. So, for now, as a temporary solution, I would propose two approaches:
Maybe during the integration implementation we will hit upon an idea how to mix them together without compromises 😉 |
Thanks for quick response @19majkel94, I fully agree. Recapitulating, let's start with temporary solutions. During the integration process, I'll dive a little bit into TypeGraphQL public API and perhaps, I'll try to adjust Nest API as possible (for example, subscriptions API). Due to that, it should be easier to provide better integration at some point :) I'll let you know if I've come across some potential idea how to connect them in an even more solid way, please, let me know too! |
@kamilmysliwiec @19majkel94 I made a module for nestjs, manually combining decorators, type-graphql will create the schema and nestjs will provide the resolvers |
Would be much better, if type-graphql was a peerDependency. |
|
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
I'm submitting a...
Current behavior
Currently I have to define both:
cats.graphql
file andcats.resolver.ts
along withcats.dto.ts
. I clearly see quite a lot of redundancy in those files, it's huge space for endless mistakes from developers synchronizing those. Decorators handle resolver mapping, but.graphql
file or files could be generated from typescript code too (https://github.com/19majkel94/type-graphql does it).Expected behavior
I would love
type-graphql
library to be integrated in @nest/graphql. I would do this on my own and submit pull request, but graphql package is quite a lot of code to analyze.Minimal reproduction of the problem with instructions
Currently to remove redundancy in files I have to use
type-graphql
library as a workaround, doubling decorators (or merging them both into one). For example:app.module.ts
auth.resolver.ts
In above example everything works perfectly and I get automatically-generated on application lift
schema.graphql
file. No need to leave world-of-typescript for graphql schemes because they get generated:Also,
graphql.schema.ts
file becomes unnecessary in such setup. The only bad thing is that these setup is not part of@nestjs/graphql
package! Is there any good reason for that?What is the motivation / use case for changing the behavior?
To improve
@nestjs/graphql
to the next level!The text was updated successfully, but these errors were encountered: