Skip to content
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

Integration with Nest #135

Open
stevefan1999-personal opened this issue Aug 29, 2018 · 85 comments
Open

Integration with Nest #135

stevefan1999-personal opened this issue Aug 29, 2018 · 85 comments
Labels
Community 👨‍👧 Something initiated by a community Discussion 💬 Brainstorm about the idea Enhancement 🆕 New feature or request
Milestone

Comments

@stevefan1999-personal
Copy link

stevefan1999-personal commented Aug 29, 2018

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.

@Salimlou
Copy link

HI @stevefan1999 ,

I am the author of that package, and here is the source https://github.com/MagnusCloudCorp/nestjs-type-graphql,

But it's really really basic integration I made for a small project, just a matter to respect NestJs lifecycle.

You can easily put the same code inside your app, so you have full control.

If you have any question!

@MichalLytek
Copy link
Owner

MichalLytek commented Aug 29, 2018

The goal of TypeGraphQL integration with Nest is to create a replacement/substitute for the GraphQL (Apollo) module for Nest, so all the Nest features like pipes or guards would work. This requires a lot of changes in TypeGraphQL core so it will be done later 😞

@kamilmysliwiec
Copy link

Would love to see it! ❤️

@MichalLytek MichalLytek added Enhancement 🆕 New feature or request Community 👨‍👧 Something initiated by a community Discussion 💬 Brainstorm about the idea labels Aug 31, 2018
@MichalLytek MichalLytek added this to the Future release milestone Aug 31, 2018
@Sacro
Copy link

Sacro commented Sep 20, 2018

I have this working without needing anything fancy

import { Injectable, Logger } from '@nestjs/common';
import { GqlOptionsFactory, GqlModuleOptions } from '@nestjs/graphql';
import { buildSchema } from 'type-graphql';

@Injectable()
export class GraphqlConfigService implements GqlOptionsFactory {
  async createGqlOptions(): Promise<GqlModuleOptions> {
    const schema = await buildSchema({
      resolvers: [__dirname + '../**/*.resolver.ts'],
    });

    return {
      debug: true,
      playground: true,
      schema,
    };
  }
}

And then in the app.module...

  imports: [
    GraphQLModule.forRootAsync({
      useClass: GraphqlConfigService,
    }),
  ],

@stevefan1999-personal
Copy link
Author

@Sacro Do you have injects available for TGQL resolvers in runtime?

@Sacro
Copy link

Sacro commented Sep 21, 2018

I'm not sure I follow.

My current repo is at https://gitlab.benwoodward.me.uk/pokedex/backend

@stevefan1999-personal
Copy link
Author

stevefan1999-personal commented Sep 21, 2018

@Sacro In this case, you aren't using NestJS injections at all in resolvers far as I see, you also used Active Record pattern so you evaded the need for a repository. Unfortunately, I was indeed using AR pattern (with @nestjs/typeorm package) and some other providers so Injectable is definitely needed.

But the problem is, NestJS doesn't have a global container, it is re-entrant-able, so instead, the container is attached by an instance context by design.

@caseyduquettesc
Copy link

@stevefan1999-personal Injecting repositories seems to work fine for me when I build off @Sacro's awesome integration work. Here is one of my resolvers that I moved from @nestjs/graphql to type-graphql along with the original statements.

item.resolvers.ts

// import { Resolver, Query } from '@nestjs/graphql';
import { Resolver, Query } from 'type-graphql';

import { Item } from './item.entity';
import { ItemService } from './item.service';

// @Resolver('Item')
@Resolver(_of => Item)
export class ItemsResolvers {
  constructor(private readonly itemsService: ItemService) {}

  // @Query()
  @Query(_returns => [Item])
  public async getItems(): Promise<Item[]> {
    return this.itemsService.findAll();
  }
}

item.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { Item } from './item.entity';

@Injectable()
export class ItemService {
  constructor(@InjectRepository(Item) private readonly itemRepository: Repository<Item>) {}

  public async findAll(): Promise<Item[]> {
    return this.itemRepository.find();
  }
}

@kamilmysliwiec
Copy link

I just realized that integration could be fairly easy. I'll provide a POC once I find some time :)

@ghost
Copy link

ghost commented Oct 9, 2018

I just realized that integration could be fairly easy. I'll provide a POC once I find some time :)

Hello Kamil, do you have any news about this POC ? I'm trying to find the best integration method for type-graphql and Nest.js.

@anodynos
Copy link

anodynos commented Oct 9, 2018

Hey @kamilmysliwiec I found type-graphql this past weekend, which I spend looking at many possible graphql solutions (from prisma to postgraphile to type-graphql). My opinion, it rocks! Just like NestJS, you can build anything :-) I think it's the most mature and feature complete library out there, and it will be AWESOME if it works smoothly with Nest - we can't wait for nest/type-graphql ;-)

So, I've been trying a couple of days to make them work and eventually they did, but with quite a few hacks, for example:

The type-graphql @Resolver classes are injected their service props fine initially, but then type-graphql creates it's own instance of the resolver and it is using it. Of course, it misses to DI the new instance, so all injections are lost.

So I had to hack it like:

let service: TheService;
@Resolver()
export class TheResolver {  
  constructor(private _service: TheService) {
    if (_service) service = _service; // workaround for DI 
  }
  @Query(ret => Them)
  getEm() { return service.getEm(); }
}

but its not nice or scalable etc.

So if we can make this integration happen, it will be really awesome!!!

@ghost
Copy link

ghost commented Oct 10, 2018

Hey @anodynos, you can look at this working solution :

src/app/app.module.ts

import * as path from 'path';

import { APP_INTERCEPTOR } from '@nestjs/core';
import { Module, CacheModule, CacheInterceptor } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { GraphQLModule } from '@nestjs/graphql';
import { ConfigModule } from 'nestjs-config';

import { TypeOrmConfigService } from './typeorm.config.service';
import { GraphqlConfigService } from './graphql.config.service';

import { PaysModule } from 'pays/pays.module';

@Module({
  imports: [
    ConfigModule.load(path.resolve(__dirname, 'config/**/*.{ts,js}')),
    CacheModule.register(),
    TypeOrmModule.forRootAsync({ useClass: TypeOrmConfigService }),
    GraphQLModule.forRootAsync({ useClass: GraphqlConfigService }),
    PaysModule,
  ],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: CacheInterceptor,
    },
  ],
})
export class AppModule {
}

src/app/graphql.config.service.ts

import { Injectable } from '@nestjs/common';
import { GqlOptionsFactory, GqlModuleOptions } from '@nestjs/graphql';
import { ConfigService } from 'nestjs-config';
import { useContainer, buildSchema } from 'type-graphql';

@Injectable()
export class GraphqlConfigService implements GqlOptionsFactory {
  constructor(private readonly config: ConfigService) { }
  
  setContainer(container: any) {
    useContainer(container);
  }

  async createGqlOptions(): Promise<GqlModuleOptions> {
    const schema = await buildSchema({
      resolvers: [ __dirname + '../**/*.resolver.ts' ],
    });

    return {
      debug: this.config._isDev(),
      playground: this.config._isDev(),
      schema,
    };
  }
}

src/pays/pays.service.ts

import { Injectable } from '@nestjs/common';

import { Pays } from './pays.entity';

@Injectable()
export class PaysService {
 async findAll(): Promise<Pays[]> {
    return Pays.find();
  }
}

src/pays/pays.resolver.ts

import { Injectable } from '@nestjs/common';
import { Resolver, Query, Arg, Int } from 'type-graphql';

import { Pays } from './pays.entity';
import { PaysService } from './pays.service';

@Resolver(of => Pays)
export class PaysResolver {
  constructor(private readonly paysService: PaysService) { }
  
  @Query(returns => [Pays])
  async pays(): Promise<Pays[]> {
    return this.paysService.findAll();
  }
}

src/main.ts

// Nest App
const app = await NestFactory.create(AppModule, server);
const graphQlConfigService = app.get(GraphqlConfigService);
graphQlConfigService.setContainer(app);

@caseyduquettesc
Copy link

That method still won't use any guards or interceptors that you have setup in NestJS. The only way I've gotten requests to go through those is by using Nest's @Resolver decorator, which can't be used on the same class as type-graphql's @Resolver decorator.

@anodynos
Copy link

anodynos commented Oct 10, 2018

Thank you Christopher @christopherdupont - I had a similar setup at some point, didn't work :-(
Does yours work without the problem/workaround I described above?

Basicaly I took code / ideas from @Sacro https://gitlab.benwoodward.me.uk/pokedex/backend
I think my when I tried to @InjectRepository on my service and the service into the Resolver, I started having the problem I said:

@Resolver(of => Pays)                                                            
export class PaysResolver {
  constructor(private readonly paysService: PaysService) { }   // <---- THIS IS CALLED TWICE - 1st time with `paysService` instance (instantiated by NestJS) and second time by type-graphql, with `null`.

If your's doesn't have this problem, I 'd like to examine your setup - can you please put a min working example on a repo?

@caseyduquettesc Sure, there are a lot of integration points (and pains!!!) still... We're can't wait for @nest/type-graphql@~1.0 :-)

@anodynos
Copy link

anodynos commented Oct 10, 2018

Thank you @christopherdupont - I had a look and I think it works because you're using

@Entity({ name: 't_pays' })
@ObjectType()
export class Pays extends BaseEntity

I think if you start using Repository based entities or start using more DI, you'll start facing problems.

@mohaalak
Copy link

mohaalak commented Nov 15, 2018

I created a module for NestJs, combining the typegraphql and nestjs decorators, the type graphql will generate the schema from typescript and NestJS will provide the resolvers, so guards, pipes, interceptors, injection is on NestJS but schema generating is by type graphql.

https://github.com/mohaalak/nestjs_typegraphql

@kamilmysliwiec
Copy link

I have just finished POC integration locally. The biggest issue that I'm encountering frequently so far is an inconsistency between graphql libraries. Hence, I'm quite often seeing this error:
https://19majkel94.github.io/type-graphql/docs/faq.html#i-got-error-like-cannot-use-graphqlschema-object-object-from-another-module-or-realm-how-to-fix-that

which makes it almost impossible to provide a good developer experience for the end-user (calling npm dedupe is always required). This issue has been discussed here already: #192 and I see that it's not going to be fixed. However, I would love to keep type-graphql as a peer dependency (and graphql is nest peer dep as well), therefore, these libraries would be able to share the same graphql package by default. So the question goes here (cc @19majkel94): since the graphql package has to stay as a normal dependency of the type-graphql, could you expose another function which works same as buildSchema but instead of returning a schema, it would additionally call printSchema(schema) (from internal graphql-tools) and return us a string? :) For now (as a workaround), I have to emit schema to the file and afterward, read file content to get the stringified schema (what heavily affects library performance).

@MichalLytek
Copy link
Owner

@kamilmysliwiec

However, I would love to keep type-graphql as a peer dependency (and graphql is nest peer dep as well), therefore, these libraries would be able to share the same graphql package by default.

The problem is that TypeGraphQL heavily depends on graphql-js (and @types/graphql) and it's hard to control the version using peer dependencies mechanism. I can bump major version (with 0.x.y it's not a problem) when bumping peer deps minor version of graphql-js but this might fail in runtime (cannot read property 'directives' of undefined) as many people ignore npm peer deps warnings in console on install.

could you expose another function which works same as buildSchema but instead of returning a schema, it would additionally call printSchema(schema) (from internal graphql-tools) and return us a string? :)

emitSchemaFile options (as well as emitSchemaDefinitionFile helper function) are only a 3-lines wrapper around printSchema and writeFile. It doesn't change the schema building pipeline, it only acts as a helper for better DX because many people think that buildSchema return a special object, not a standard graphql-js schema.

So all you need to do is:

const schema = await buildSchema(options);
const schemaString = printSchema(schema);

I also have a PoC of building apollo-like resolversMap on a branch, this might help you with the integration:
https://github.com/19majkel94/type-graphql/tree/resolvers-map

@kamilmysliwiec
Copy link

Actually, I took a look into the source code of these functions already. The problem is that when I have tried to do the exact same thing as you proposed above^

const schema = await buildSchema(options);
const schemaString = printSchema(schema);

I was also getting the graphql inconsistency error, because in this case, buildSchema uses nested graphql dependency of type-graphql (and schema is built using this internal version) while my imported printSchema would come from another graphql package.

@MichalLytek
Copy link
Owner

@kamilmysliwiec
You're right, I will try to finish the work on generating typedefs-resolvers instead of the executable schema as soon as possible.

I will also try to find a way to detect the version of graphql in node_modules to throw error when peer dependency version isn't correct. I see that the whole ecosystem is switching to having graphql as a peer dependency, so this will solve a lot of compatibility problems.

And sorry for the late response 😞

@peterbabic
Copy link

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

@devniel
Copy link

devniel commented Sep 7, 2019

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
Copy link

unlight commented Feb 29, 2020

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
Copy link
Owner

@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
Copy link

unlight commented Mar 5, 2020

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

@EndyKaufman
Copy link

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

@unlight
Copy link

unlight commented Mar 14, 2020

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
Copy link
Owner

MichalLytek commented Mar 21, 2020

@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 #135 (comment) 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
Copy link

@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
Copy link

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

@EndyKaufman
Copy link

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
Copy link

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
Copy link

@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
Copy link

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

@MichalLytek
Copy link
Owner

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
Copy link

RafaelKr commented Apr 3, 2020

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
Copy link
Owner

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
Copy link

RafaelKr commented Apr 3, 2020

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
Copy link
Owner

MichalLytek commented Apr 3, 2020

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
Copy link

devniel commented Apr 3, 2020

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
Copy link

XBeg9 commented Apr 3, 2020

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

@MichalLytek
Copy link
Owner

@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
Copy link

mi3lix9 commented May 8, 2020

Will guards and pipes be supported in the future?

@theMitch36
Copy link

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

@wSedlacek
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community 👨‍👧 Something initiated by a community Discussion 💬 Brainstorm about the idea Enhancement 🆕 New feature or request
Projects
None yet
Development

No branches or pull requests