MichalLytek / type-graphql

Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
https://typegraphql.com
MIT License
8.03k stars 676 forks source link

Integration with Nest #135

Open stevefan1999-personal opened 6 years ago

stevefan1999-personal commented 6 years ago

Owner note

Scroll to typegraphql-nestjs package info ⬇️

Original post

I was wondering if there could be more support for Nest.js, in particular, integration with an authorization endpoint is needed. Right now, I'm using this NPM package + TypeORM, following the sample, so far so good but unfortunately the source was nowhere to be found, and so I have to workaround my auth checker which required me to pass the database module into the context but it is very dangerous. So what about an official extension of TypeGraphQL to Nest.JS? This would make it easier to write universal services.

michelcve commented 5 years ago

@kamilmysliwiec I'm having difficulties with the integration, even your example (23-type-graphql) does not seem to work. I always get a 'Cannot return null for non-nullable field Recipe.id.'

I filed a nest.js issue here.

Not sure if it's nest.js or type-graphql related, so that's why I'm linking it here as well.

pContat commented 5 years ago

@michelcve , we had the same error and it turned out that was the import: we imported resolver from type-graphql instead of @nestjs/graphql. Be careful to import from '@nestjs/graphql' and not 'type-graphql'

michelcve commented 5 years ago

@Uldax Thanks a lot, that indeed did the trick!

Furthermore, the example program didn't work because the service isn't implemented. I got a similar looking error as I got in my own program, so that threw me off.

Hossein-s commented 5 years ago

It doesn't integrate type-graphql cool features like union types, enums, etc. I wish param decorators would bring us a way to fully integrate type-graphql and nestjs amazing features.

kamilmysliwiec commented 5 years ago

@Hossein-s you should be able to use both union types and enums easily.

michelcve commented 5 years ago

What I did notice is that the @ResolveProperty seems to be broken. No matter what I do, as soon as I add @ResolveProperty my application hangs.

It's probably related to this bug

Hossein-s commented 5 years ago

@Hossein-s you should be able to use both union types and enums easily.

I have checked source code of @nestjs/graphql and noticed that you are using type-graphql only for generating type definitions, not the executable schema.

So type-graphql can't generate things like enum mappings (mapping enum to internal value) and union type resolvers (resolveType method on union) this way. I'm not sure but things like inheritance and generic types may not work too.

It may be better to delegate generating executable schema to type-graphql and then wrap resolvers with Nest executors for code first approach.

Hossein-s commented 5 years ago

To generate a framework-context-aware resolvers, frameworks should hook in generation of executable schema.

One idea is to have a resolver factory function that takes some info needed for reflection (class reference, method, resolver type, etc...) and pass it to TypeGraphQL's buildSchema function to be applied when generating resolver.

@19majkel94 What do you think? 😃

manzonif commented 5 years ago

Hi, I would like to run the following middleware in my Nestjs backend, but it is not clear to me if it is possible to implement it with the functions currently present in NestJs, (Interceptors?) Or if I have to wait for the type-graphql middlewares to be supported in NestJs.

import { MiddlewareFn } from 'type-graphql';
import { Model, Document } from 'mongoose';
import { getClassForDocument } from 'typegoose';

export const TypegooseMiddleware: MiddlewareFn = async (_, next) => {
  const result = await next();

  if (Array.isArray(result)) {
    return result.map(item => (item instanceof Model ? convertDocument(item) : item));
  }

  if (result instanceof Model) {
    return convertDocument(result);
  }

  return result;
};

function convertDocument(doc: Document) {
  const convertedDocument = doc.toObject();
  const DocumentClass: Function = getClassForDocument(doc);
  Object.setPrototypeOf(convertedDocument, DocumentClass.prototype);
  return convertedDocument;
}
manzonif commented 5 years ago

May be, something like this:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Model, Document } from 'mongoose';
import { getClassForDocument } from 'typegoose';

export interface Response<T> {
  data: T;
}

@Injectable()
export class TypegooseInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
    return next.handle().pipe(map(data => {
      if (Array.isArray(data)) {
        return data.map(item => (item instanceof Model ? convertDocument(item) : item));
      }

      if (data instanceof Model) {
        return convertDocument(data);
      }

      return data;
    }));
  }
}

function convertDocument(doc: Document): any {
  const convertedDocument = doc.toObject();
  const DocumentClass: Function = getClassForDocument(doc);
  Object.setPrototypeOf(convertedDocument, DocumentClass.prototype);
  return convertedDocument;
}
peterbabic commented 5 years ago

Please, can someone look at https://github.com/nestjs/nest/issues/2187, if it is related - my understanding of the topic is too low. Thank You

devniel commented 5 years ago

I have found a simple workaround to generate an schema using the type-graphql annotations and then load it with nestjs, just overload it, in my case is fine because I'm using it to have a mock graphql schema instead of the generated with nestjs avoiding to mock the repositories and so on. I needed to apply the addMockFunctionsToSchema method to an schema generated with the raw buildSchema from type-graphql that only works well with resolvers annotated with the type-graphql annotations.

import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { Ctx, Query as QueryTypeGraphql, Mutation as MutationTypeGraphql, Arg } from 'type-graphql';

@Resolver(of => User)
export class UsersResolver {

  /**
   * Returns the current logger user
   */
  @QueryTypeGraphql(() => User, { 
    nullable: true,
    description: 'Returns the current logged user.'
  })
  @Query(() => User, { 
    nullable: true,
    description: 'Returns the current logged user.'
  })
  @UseGuards(UserGuard)
  async me(@Ctx() ctx) : Promise<User | undefined> {
   // ...
  }

 /**
  * Register a new user
  */
  @MutationTypeGraphql((): typeof User => User, { 
    description: 'Register a new user'
  })
  @Mutation((): typeof User => User, {
    description: 'Register a new user'
  })
  public async create(
    @Args('data')
    @Arg('data')
    {
      email,
      name,
      username,
      password,
    }: UserCreateInput): Promise<User> {
      // ...
    }
}

With that the schema can be mocked an then passed to nestjs graphql module.

// Generate a base schema from classes and decorators
    let baseSchema;
    try{
      baseSchema = await buildSchema({
        resolvers: [ __dirname + "/**/*.resolver.ts"],
      });
    }catch(e){
      console.error(e.details);
      throw e;
    }

    // Extract only types definitions
    const schemaString = printSchema(baseSchema);

    // Make a GraphQL schema with no resolvers
    const schema = makeExecutableSchema({ typeDefs: schemaString });

    // Add mocks, modifies schema in place
    addMockFunctionsToSchema({ schema });

     @Module({ 
      imports: [
        ConfigModule,
        DatabaseModule,
        UsersModule,
        AuthModule,
        GraphQLModule.forRoot({
          debug: true,
          playground: true,
          introspection: true,
          schema: schema,
     //...
unlight commented 4 years ago

I'm trying to integrate NestJs + type-graphql + Prisma 2 Facts:

  1. I want to use code first approach
  2. I want to use generated typegraphql revolvers (by node_modules/typegraphql-prisma/generator.js) Example of generated FindOneUserResolver
    
    import { Arg, Args, ArgsType, Ctx, Field, FieldResolver, Float, ID, InputType, Int, Mutation, ObjectType, Query, Resolver, Root, registerEnumType } from "type-graphql";
    import { FindOneUserArgs } from "./args/FindOneUserArgs";
    import { User } from "../../../models/User";

@Resolver(_of => User) export class FindOneUserResolver { @Query(_returns => User, { nullable: true, description: undefined }) async findOneUser(@Ctx() ctx: any, @Args() args: FindOneUserArgs): Promise<User | null> { return ctx.prisma.user.findOne(args); } }


The problem that is Nest do not see decorator `Resolver` from `type-graphql`. It expects from `@nestjs/graphql`.
I've tried to put this `FindOneUserResolver` to nest module providers, GraphQLModule options under `buildSchema -> resolvers`.

In playground I see that resolvers from type-graphql affected on schema, but they are returning null.

Is it not yet supported? Or I'm doing something wrong...

**Update**:
After playing some time I found that I must use Args, Context, Mutation, Query, ResolveProperty, Resolver decorators from '@nestjs/graphql', but still using other generated classes partially, e.g. FindManyUserArgs, etc. Playground repository - https://github.com/unlight/nest-typescript-starter
MichalLytek commented 4 years ago

@unlight You may use this fork: https://github.com/EndyKaufman/typegraphql-prisma-nestjs

As stated earlier, in the future you gonna be able to use just typegraphql-prisma when the Nest integration will support plain TypeGraphQL decorators 😉

unlight commented 4 years ago

In fact this fork is not using nestjs. All is spinning around type-graphql, prisma and direct usage of ApolloServer. :D

EndyKaufman commented 4 years ago

this repo use nestjs https://github.com/EndyKaufman/typegraphql-prisma-nestjs-example, nestjs need for use guards and pipes from nest world

unlight commented 4 years ago

Nest v7 broke my nest+graphql+prisma2 app. https://docs.nestjs.com/migration-guide#graphql

In order to migrate your existing application, simply rename all the type-graphql imports to the @nestjs/graphql

Looks like importing decorators from type-graphql is not compatible anymore. How we can fix generated models, args, etc.? Is it possible to add option to generator typegraphql to specify import moduleSpecifier?

MichalLytek commented 4 years ago

@unlight I'm afraid that due to embedding the fork of TypeGraphQL inside NestJS 7, the official TypeGraphQL - Prisma 2 integration won't gonna work with the new Nest's GraphQL layer anymore.

Please check out @EndyKaufman Nest-based fork or create an issue on Nest GraphQL repository with a feature request.

Basically, we've returned to the starting point, where we need to revamp the simple integrations like https://github.com/MichalLytek/type-graphql/issues/135#issuecomment-423345833 or nestjs-type-graphql for those people that want to use TypeGraphQL, not a Nest's variant.

As stated a year ago, somewhere in the future there will be available fully-fledged integration that will allow to use Nest's guards, interceptors, etc. in the TypeGraphQL resolvers 😉

wSedlacek commented 4 years ago

@unlight I do have a PR in @EndyKaufman Nest-based fork to support NestJS 7. To fix the imports is quite simple however the problem is deciding what to do for NestJS <= 6.

clayrisser commented 4 years ago

Support nest 7 and then have a fork that supports nest 6. Just an idea.

EndyKaufman commented 4 years ago

Original Typegraphql inputs and objects can use in frontend, when sharing it with mono repository between backend and frontend, but in nestjs 7 is break it, now I try add support this feature for nestjs 7

EndyKaufman commented 4 years ago

If use only in backend fork work may work correct, but I use inputs and object not only backend, I use it in frontend on angular

wSedlacek commented 4 years ago

@EndyKaufman To avoid issues like that my work flow is as follows:

Backend:

  1. Setup Prisma Schema
  2. Generate NestJS Resolvers using TypeGraphQL Generator
  3. Import into NestJS modules.

Frontend:

  1. Write GraphQL Queries/Mutations
  2. Generate Apollo Angular request services using GraphQL Code Generator
  3. Import into Angular services.

This means I only ever need to write the Prisma Schema and the GraphQL request and I get type safety on the frontend and backend and my GraphQL request can be as simple or complicated as I want them to be.

It also means that the server and client are decoupled in a sense that neither project are importing from the same library but still type safe through code generation.

clayrisser commented 4 years ago

@wSedlacek I've been doing the same thing, except I'm using react :)

MichalLytek commented 4 years ago

I've spend yesterday's afternoon on working on the basic integration of TypeGraphQL with NestJS, and here it is - typegraphql-nestjs 🎉

This integration provides a way to use TypeGraphQL with NestJS modules and dependency injector. For now it doesn't support other NestJS features, so you have to use standard TypeGraphQL equivalents.

The "how to use it" instruction and some code examples are available on the repo: https://github.com/MichalLytek/typegraphql-nestjs

Plese check it out and let me know how it works for you guys (or which features are missing, apart from the guards or pipes) 😉

RafaelKr commented 4 years ago

That's awesome news! Now I wonder if this makes @nestjs/graphql obsolete?

@kamilmysliwiec Will this be the new official way to combine NestJS with TypeGraphQL in the future?

MichalLytek commented 4 years ago

Will this be the new official way to combine NestJS with TypeGraphQL in the future?

They have copied and embedded the modified TypeGraphQL source code into @nestjs/graphql so I doubt that they will be interested in making integration with TypeGraphQL as you can just use their code-first solution 😉

RafaelKr commented 4 years ago

I'm just about to start a new project. And I did understand that @nestjs/graphql already uses type-graphql under the hood. That's why I'm asking if it's recommended to use @nestjs/graphql or your new typegraphql-nestjs.

I also read this part in https://www.npmjs.com/package/typegraphql-nestjs#caveats:

Moreover, with typegraphql-nestjs you can also take advantage of additional features (comparing to @nestjs/graphql) like inline field resolvers, query complexity or Prisma 2 integration.

But I do not quite understand what the difference is.

MichalLytek commented 4 years ago

And I did understand that @nestjs/graphql already uses type-graphql under the hood.

https://trilon.io/blog/announcing-nestjs-7-whats-new#GraphQL-TypeScript

devniel commented 4 years ago

Hi, @RafaelKr , @nestjs/graphql doesn't use type-graphql, you can check its package.json, I think that it has mimicked the code to be "injectable" so it could work with the NestJS DI system. For instance, if you use @nestjs/graphql, you write your resolvers the same way as type-graphql and then you want to use those resolvers with some type-graphql methods such as buildSchema, it won't work because the types are different. That issue is solved with this plugin (I don't know about the DI issues), thanks @MichalLytek 🎉 !!

XBeg9 commented 4 years ago

@MichalLytek is there a possible workaround to get pipes, guards working using https://www.npmjs.com/package/typegraphql-nestjs?

MichalLytek commented 4 years ago

@XBeg9 They use their own ExternalContextCreator to generate GraphQL resolver functions, so it's not possible for now to use Nest's pipes or guards using TypeGraphQL execution stack.

mi3lix9 commented 4 years ago

Will guards and pipes be supported in the future?

theMitch36 commented 4 years ago

has anyone (@MichalLytek) gotten Nestjs to work with graphql code-first in lambda?

wSedlacek commented 4 years ago

has anyone (@MichalLytek) gotten Nestjs to work with graphql code-first in lambda?

Are you referring to AWS Lambda Functions? You should check out this repo https://github.com/lnmunhoz/nestjs-graphql-serverless

The main difference from a regular NestJS application is the way the entrypoint works. In this repo they use serverless.ts as the entry point.