Skip to content

Commit 37b76bc

Browse files
committed
Initial version
0 parents  commit 37b76bc

37 files changed

+5287
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
build
4+
package

.prettierrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"arrowParens": "avoid",
3+
"trailingComma": "all"
4+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Michał Lytek
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<p align="center">
2+
<img alt="typegraphql logo" src="https://raw.githubusercontent.com/MichalLytek/typegraphql-nestjs/master/typegraphql-logo.png" width="300" height="200">
3+
<img alt="nest logo" src="https://nestjs.com/img/logo_text.svg" width="300" height="200">
4+
</p>
5+
6+
# TypeGraphQL NestJS Module
7+
8+
Basic integration of [TypeGraphQL](https://typegraphql.com/) in [NestJS](https://nestjs.com/).
9+
10+
Allows to use TypeGraphQL features while integrating with NestJS modules system and dependency injector.
11+
12+
## Installation
13+
14+
```sh
15+
npm i typegraphql-nestjs @nestjs/graphql
16+
```
17+
18+
or
19+
20+
```sh
21+
yarn add typegraphql-nestjs @nestjs/graphql
22+
```
23+
24+
## How to use?
25+
26+
The `typegraphql-nestjs` package exports `TypeGraphQLModule` dynamic module, which is based on the official NestJS `GraphQLModule`.
27+
28+
It exposes two static methods. The first one is `TypeGraphQLModule.forRoot()` which you should call on your root module, just like with `GraphQLModule`.
29+
30+
The only difference is that as its argument you can provide [typical TypeGraphQL `buildSchema` options](https://typegraphql.com/docs/bootstrap.html) like `emitSchemaFile` or `authChecker` apart from the [standard `GqlModuleOptions` from `@nestjs/graphql`](https://docs.nestjs.com/graphql/quick-start#installation) like `installSubscriptionHandlers` or `context`:
31+
32+
```ts
33+
import { Module } from "@nestjs/common";
34+
import { TypeGraphQLModule } from "typegraphql-nestjs";
35+
36+
import RecipeModule from "./recipe/module";
37+
import { authChecker } from "./auth";
38+
39+
@Module({
40+
imports: [
41+
TypeGraphQLModule.forRoot({
42+
emitSchemaFile: true,
43+
validate: false,
44+
authChecker,
45+
dateScalarMode: "timestamp",
46+
context: ({ req }) => ({ currentUser: req.user }),
47+
}),
48+
RecipeModule,
49+
],
50+
})
51+
export default class AppModule {}
52+
```
53+
54+
Then, inside the imported modules (like `RecipeModule`) you just need to register the resolvers classes in the module `providers` array:
55+
56+
```ts
57+
import { Module } from "@nestjs/common";
58+
59+
import RecipeResolver from "./resolver";
60+
import RecipeService from "./service";
61+
62+
@Module({
63+
providers: [RecipeResolver, RecipeService],
64+
})
65+
export default class RecipeModule {}
66+
```
67+
68+
And that's it! 😁
69+
70+
Notice that the resolvers classes are automatically inferred from your submodules `providers` array, so you don't need to specify `resolvers` property from TypeGraphQL `buildSchema` options inside `TypeGraphQLModule.forRoot()`.
71+
72+
In case of need to provide `orphanedTypes` setting, you should use `TypeGraphQLModule.forFeature()`. The recommended place for that is in the module where the orphaned type (like `SuperRecipe`) belongs:
73+
74+
```ts
75+
import { Module } from "@nestjs/common";
76+
import { TypeGraphQLModule } from "typegraphql-nestjs";
77+
78+
import RecipeResolver from "./resolver";
79+
import RecipeService from "./service";
80+
import { SuperRecipe } from "./types";
81+
82+
@Module({
83+
imports: [
84+
TypeGraphQLModule.forFeature({
85+
orphanedTypes: [SuperRecipe],
86+
}),
87+
],
88+
providers: [RecipeResolver, RecipeService],
89+
})
90+
export default class RecipeModule {}
91+
```
92+
93+
Using `.forFeature()` ensures proper schemas isolation and automatically supply `orphanedTypes` option for underlying `buildSchema` from TypeGraphQL - again, there's no need to provide it manually in `.forRoot()` options.
94+
95+
## Caveats
96+
97+
While this integration provides a way to use TypeGraphQL with NestJS modules and dependency injector, for now it doesn't support [other NestJS features](https://docs.nestjs.com/graphql/tooling) like guards, interceptors, filters and pipes.
98+
99+
To achieve the same goals, you can use standard TypeGraphQL equivalents - middlewares, custom decorators, built-in authorization and validation.
100+
101+
Moreover, with `typegraphql-nestjs` you can also take advantage of additional features (comparing to `@nestjs/graphql`) like [inline field resolvers](https://typegraphql.com/docs/resolvers.html#field-resolvers), [interface args and resolvers](https://typegraphql.com/docs/next/interfaces.html#resolvers-and-arguments), [query complexity](https://typegraphql.com/docs/complexity.html) or [Prisma 2 integration](https://github.com/MichalLytek/type-graphql/blob/prisma/Readme.md).
102+
103+
## Examples
104+
105+
You can see some examples of the integration in this repo:
106+
107+
1. [Basics](https://github.com/MichalLytek/typegraphql-nestjs/tree/master/examples/1-basics)
108+
109+
Basics of the integration, like `TypeGraphQLModule.forRoot` usage
110+
111+
1. [Multiple Servers](https://github.com/MichalLytek/typegraphql-nestjs/tree/master/examples/2-multiple-servers)
112+
113+
Advanced usage of multiple schemas inside single NestJS app - demonstration of schema isolation in modules and `TypeGraphQLModule.forFeature` usage
114+
115+
You can run them by using `ts-node`, like `npx ts-node ./examples/1-basics/index.ts`.
116+
117+
All folders contain a `query.graphgql` file with some examples operations you can perform on the GraphQL servers.

examples/1-basics/app.module.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Module } from "@nestjs/common";
2+
import { TypeGraphQLModule } from "../../src";
3+
4+
import RecipeModule from "./recipe/module";
5+
6+
@Module({
7+
imports: [
8+
TypeGraphQLModule.forRoot({
9+
emitSchemaFile: true,
10+
validate: false,
11+
}),
12+
RecipeModule,
13+
],
14+
})
15+
export default class AppModule {}

examples/1-basics/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import "reflect-metadata";
2+
import { NestFactory } from "@nestjs/core";
3+
import {
4+
NestFastifyApplication,
5+
FastifyAdapter,
6+
} from "@nestjs/platform-fastify";
7+
8+
import AppModule from "./app.module";
9+
10+
async function bootstrap() {
11+
const app = await NestFactory.create<NestFastifyApplication>(
12+
AppModule,
13+
new FastifyAdapter(),
14+
);
15+
16+
await app.listen(3000);
17+
}
18+
19+
bootstrap();

examples/1-basics/query.gql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
query GetRecipes {
2+
recipes {
3+
title
4+
description
5+
}
6+
}
7+
8+
mutation AddRecipe {
9+
addRecipe(input: { title: "My Recipe", description: "My description" }) {
10+
title
11+
description
12+
}
13+
}

examples/1-basics/recipe/module.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Module } from "@nestjs/common";
2+
3+
import RecipeResolver from "./resolver";
4+
import RecipeService from "./service";
5+
6+
@Module({
7+
providers: [RecipeResolver, RecipeService],
8+
})
9+
export default class RecipeModule {}

examples/1-basics/recipe/resolver.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Injectable } from "@nestjs/common";
2+
import { Resolver, Query, Mutation, Arg } from "type-graphql";
3+
4+
import RecipeService from "./service";
5+
import Recipe from "./type";
6+
7+
@Injectable()
8+
@Resolver()
9+
export default class RecipeResolver {
10+
constructor(private readonly recipeService: RecipeService) {}
11+
12+
@Query(returns => [Recipe])
13+
recipes() {
14+
return this.recipeService.getRecipes();
15+
}
16+
17+
@Mutation(returns => Recipe)
18+
addRecipe(@Arg("input") recipe: Recipe) {
19+
this.recipeService.addRecipe(recipe);
20+
return recipe;
21+
}
22+
}

examples/1-basics/recipe/service.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Injectable } from "@nestjs/common";
2+
3+
import Recipe from "./type";
4+
5+
@Injectable()
6+
export default class RecipeService {
7+
private readonly recipes: Recipe[] = [];
8+
9+
getRecipes() {
10+
return this.recipes;
11+
}
12+
13+
addRecipe(recipe: Recipe) {
14+
this.recipes.push(recipe);
15+
}
16+
}

examples/1-basics/recipe/type.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ObjectType, Field, InputType } from "type-graphql";
2+
3+
@ObjectType()
4+
@InputType("RecipeInput")
5+
export default class Recipe {
6+
@Field()
7+
title!: string;
8+
9+
@Field({ nullable: true })
10+
description?: string;
11+
}

examples/1-basics/schema.gql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -----------------------------------------------
2+
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
3+
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
4+
# -----------------------------------------------
5+
6+
type Mutation {
7+
addRecipe(input: RecipeInput!): Recipe!
8+
}
9+
10+
type Query {
11+
recipes: [Recipe!]!
12+
}
13+
14+
type Recipe {
15+
description: String
16+
title: String!
17+
}
18+
19+
input RecipeInput {
20+
description: String
21+
title: String!
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Module } from "@nestjs/common";
2+
import { TypeGraphQLModule } from "../../../src";
3+
4+
import AnimalResolver from "./resolver";
5+
import AnimalService from "./service";
6+
import { SuperAnimal } from "./types";
7+
8+
@Module({
9+
imports: [
10+
TypeGraphQLModule.forFeature({
11+
orphanedTypes: [SuperAnimal],
12+
}),
13+
],
14+
providers: [AnimalResolver, AnimalService],
15+
})
16+
export default class AnimalModule {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Injectable } from "@nestjs/common";
2+
import { Resolver, Query, Mutation, Arg } from "type-graphql";
3+
4+
import AnimalService from "./service";
5+
import { Animal } from "./types";
6+
7+
@Injectable()
8+
@Resolver()
9+
export default class AnimalResolver {
10+
constructor(private readonly animalService: AnimalService) {}
11+
12+
@Query(returns => [Animal])
13+
animals() {
14+
return this.animalService.getAnimals();
15+
}
16+
17+
@Mutation(returns => Animal)
18+
addAnimal(@Arg("input") animal: Animal) {
19+
this.animalService.addAnimal(animal);
20+
return animal;
21+
}
22+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Injectable } from "@nestjs/common";
2+
3+
import { Animal } from "./types";
4+
5+
@Injectable()
6+
export default class AnimalService {
7+
private readonly animals: Animal[] = [];
8+
9+
getAnimals() {
10+
return this.animals;
11+
}
12+
13+
addAnimal(animal: Animal) {
14+
this.animals.push(animal);
15+
}
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ObjectType, Field, Int, InputType } from "type-graphql";
2+
3+
@ObjectType()
4+
@InputType("AnimalInput")
5+
export class Animal {
6+
@Field()
7+
name!: string;
8+
9+
@Field(type => Int)
10+
weight!: number;
11+
}
12+
13+
@ObjectType()
14+
export class SuperAnimal extends Animal {
15+
@Field()
16+
isSuperHero!: boolean;
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
query GetRecipes {
2+
recipes {
3+
title
4+
description
5+
}
6+
}
7+
8+
mutation AddRecipe {
9+
addRecipe(input: { title: "My Recipe", description: "My description" }) {
10+
title
11+
description
12+
}
13+
}

0 commit comments

Comments
 (0)