Repository: MichalLytek/type-graphql
Pull request #803 (2021-03-03 23:02:42)

Update buildTypeDefsAndResolvers.ts
Adds a method to build TypeDefs and Resolvers in Sync mode.

MichalLytek: Could you also add tests, update docs and add a changelog entry?
codecov[bot]: # [Codecov](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=h1) Report
> Merging [#803](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=desc) (2065c8f) into [master](https://codecov.io/gh/MichalLytek/type-graphql/commit/1b6627af9e384b7aa1c19508f2e484393bc1e37c?el=desc) (1b6627a) will **increase** coverage by `0.02%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/MichalLytek/type-graphql/pull/803/graphs/tree.svg?width=650&height=150&src=pr&token=Z6D63dJD2e)](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #803      +/-   ##
==========================================
+ Coverage   96.09%   96.11%   +0.02%     
==========================================
  Files          80       80              
  Lines        1689     1698       +9     
  Branches      355      356       +1     
==========================================
+ Hits         1623     1632       +9     
  Misses         65       65              
  Partials        1        1              
```


| [Impacted Files](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/utils/buildTypeDefsAndResolvers.ts](https://codecov.io/gh/MichalLytek/type-graphql/pull/803/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL2J1aWxkVHlwZURlZnNBbmRSZXNvbHZlcnMudHM=) | `100.00% <100.00%> (ø)` | |
| [src/utils/index.ts](https://codecov.io/gh/MichalLytek/type-graphql/pull/803/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL2luZGV4LnRz) | `100.00% <100.00%> (ø)` | |
| [src/resolvers/convert-args.ts](https://codecov.io/gh/MichalLytek/type-graphql/pull/803/diff?src=pr&el=tree#diff-c3JjL3Jlc29sdmVycy9jb252ZXJ0LWFyZ3MudHM=) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=footer). Last update [1b6627a...2065c8f](https://codecov.io/gh/MichalLytek/type-graphql/pull/803?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Carassale: > Could you also add tests, update docs and add a changelog entry?

done, do you think it would be okay?
carlocorradini: > Adds a method to build TypeDefs and Resolvers in Sync mode.

I really need this feature, thanks @Carassale! 🎉✨
Carassale: I need this feature for some projects I will be working on soon. @MichalLytek do you think it can be approved?
MichalLytek: @Carassale I've added some docs change, smaller test suite, improved naming and changelog entry. Now it's ready to merge 💪

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 214fc0c9..048c2c37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ ### Features
 - allow deprecating input fields and args (#794)
 - support disabling inferring default values (#793)
 - support readonly arrays for roles of `@Authorized` decorator (#935)
+- add sync version of `buildTypeDefsAndResolvers` function (#803)
 ### Fixes
 - **Breaking Change**: properly emit types nullability when `defaultValue` is provided and remove `ConflictingDefaultWithNullableError` error (#751)
 - allow defining extension on field resolver level for fields also defined as a property of the class (#776)
diff --git a/docs/bootstrap.md b/docs/bootstrap.md
index 90f09faa..433bc003 100644
--- a/docs/bootstrap.md
+++ b/docs/bootstrap.md
@@ -139,4 +139,12 @@ ## Create typeDefs and resolvers map
 // ...the rest of `ApolloClient` initialization code
 ```
 
-Be aware that some of the TypeGraphQL features (i.a. [query complexity](complexity.md)) might not work with the `buildTypeDefsAndResolvers` approach because they use some low-level `graphql-js` features.
+There's also a sync version of it - `buildTypeDefsAndResolversSync`:
+
+```typescript
+const { typeDefs, resolvers } = buildTypeDefsAndResolvers({
+  resolvers: [FirstResolver, SecondResolver],
+});
+```
+
+However, be aware that some of the TypeGraphQL features (i.a. [query complexity](complexity.md)) might not work with the `buildTypeDefsAndResolvers` approach because they use some low-level `graphql-js` features.
diff --git a/src/utils/buildTypeDefsAndResolvers.ts b/src/utils/buildTypeDefsAndResolvers.ts
index abafb39d..5c0ada7a 100644
--- a/src/utils/buildTypeDefsAndResolvers.ts
+++ b/src/utils/buildTypeDefsAndResolvers.ts
@@ -1,10 +1,19 @@
-import { printSchema } from "graphql";
+import { GraphQLSchema, printSchema } from "graphql";
 
-import { BuildSchemaOptions, buildSchema } from "./buildSchema";
+import { BuildSchemaOptions, buildSchema, buildSchemaSync } from "./buildSchema";
 import { createResolversMap } from "./createResolversMap";
 
 export async function buildTypeDefsAndResolvers(options: BuildSchemaOptions) {
   const schema = await buildSchema(options);
+  return createTypeDefsAndResolversMap(schema);
+}
+
+export function buildTypeDefsAndResolversSync(options: BuildSchemaOptions) {
+  const schema = buildSchemaSync(options);
+  return createTypeDefsAndResolversMap(schema);
+}
+
+function createTypeDefsAndResolversMap(schema: GraphQLSchema) {
   const typeDefs = printSchema(schema);
   const resolvers = createResolversMap(schema);
   return { typeDefs, resolvers };
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 20f8f466..fae4db30 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,5 +1,8 @@
 export { buildSchema, buildSchemaSync, BuildSchemaOptions } from "./buildSchema";
-export { buildTypeDefsAndResolvers } from "./buildTypeDefsAndResolvers";
+export {
+  buildTypeDefsAndResolvers,
+  buildTypeDefsAndResolversSync,
+} from "./buildTypeDefsAndResolvers";
 export { createResolversMap } from "./createResolversMap";
 export {
   emitSchemaDefinitionFile,


Test patch:
diff --git a/tests/functional/typedefs-resolvers.ts b/tests/functional/typedefs-resolvers.ts
index e2dfcfdd..685c68d1 100644
--- a/tests/functional/typedefs-resolvers.ts
+++ b/tests/functional/typedefs-resolvers.ts
@@ -8,7 +8,6 @@ import {
   TypeKind,
   IntrospectionObjectType,
   IntrospectionInputObjectType,
-  Kind,
   IntrospectionNamedTypeRef,
   IntrospectionEnumType,
   IntrospectionUnionType,
@@ -29,6 +28,7 @@ import {
   Resolver,
   Query,
   buildTypeDefsAndResolvers,
+  buildTypeDefsAndResolversSync,
   InterfaceType,
   ObjectType,
   Field,
@@ -47,557 +47,641 @@ import {
   ResolverOptions,
 } from "../../src";
 
-describe("buildTypeDefsAndResolvers", () => {
-  const timestamp = 1547398942902;
-  let typeDefs: string;
-  let resolvers: ResolversMap;
-  let schemaIntrospection: IntrospectionSchema;
-  let schema: GraphQLSchema;
-  let pubSub: PubSubEngine;
-  let inputValue: any;
-  let enumValue: any;
-  let middlewareLogs: string[];
-
-  beforeEach(async () => {
-    middlewareLogs = [];
-    enumValue = undefined;
-  });
-
-  beforeAll(async () => {
-    getMetadataStorage().clear();
-
-    @Service()
-    class SampleService {
-      getSampleString() {
-        return "SampleString";
-      }
-    }
-
-    @InterfaceType()
-    abstract class SampleInterface {
-      @Field()
-      sampleInterfaceStringField: string;
-    }
-
-    @ObjectType({ implements: SampleInterface })
-    class SampleType1 implements SampleInterface {
-      @Field()
-      sampleInterfaceStringField: string;
-      @Field({ description: "sampleType1StringFieldDescription" })
-      sampleType1StringField: string;
-    }
-
-    @ObjectType({ implements: SampleInterface })
-    class SampleType2 implements SampleInterface {
-      @Field()
-      sampleInterfaceStringField: string;
-      @Field({ deprecationReason: "sampleType2StringFieldDeprecation" })
-      sampleType2StringField: string;
-    }
-
-    @ObjectType()
-    class SampleType3 {
-      @Field()
-      sampleInterfaceStringField: string;
-      @Field()
-      sampleType3StringField: string;
-    }
-
-    @InputType()
-    class SampleInput {
-      @Field()
-      @MinLength(10)
-      sampleInputStringField: string;
-      @Field()
-      sampleInputDefaultStringField: string = "sampleInputDefaultStringField";
-    }
-
-    enum SampleNumberEnum {
-      OptionOne,
-      OptionTwo,
-    }
-    registerEnumType(SampleNumberEnum, { name: "SampleNumberEnum" });
-
-    enum SampleStringEnum {
-      OptionOne = "OptionOneString",
-      OptionTwo = "OptionTwoString",
-    }
-    registerEnumType(SampleStringEnum, { name: "SampleStringEnum" });
-
-    const SampleUnion = createUnionType({
-      types: () => [SampleType2, SampleType3],
-      name: "SampleUnion",
-      description: "SampleUnion description",
+describe("typeDefs and resolvers", () => {
+  describe("buildTypeDefsAndResolvers", () => {
+    const timestamp = 1547398942902;
+    let typeDefs: string;
+    let resolvers: ResolversMap;
+    let schemaIntrospection: IntrospectionSchema;
+    let schema: GraphQLSchema;
+    let pubSub: PubSubEngine;
+    let inputValue: any;
+    let enumValue: any;
+    let middlewareLogs: string[];
+
+    beforeEach(async () => {
+      middlewareLogs = [];
+      enumValue = undefined;
     });
 
-    const SampleResolveUnion = createUnionType({
-      types: () => [SampleType2, SampleType3],
-      name: "SampleResolveUnion",
-      resolveType: value => {
-        if ("sampleType2StringField" in value) {
-          return "SampleType2";
-        }
-        if ("sampleType3StringField" in value) {
-          return "SampleType3";
+    beforeAll(async () => {
+      getMetadataStorage().clear();
+
+      @Service()
+      class SampleService {
+        getSampleString() {
+          return "SampleString";
         }
-        return;
-      },
-    });
+      }
 
-    @Service()
-    @Resolver()
-    class SampleResolver {
-      constructor(private readonly sampleService: SampleService) {}
+      @InterfaceType()
+      abstract class SampleInterface {
+        @Field()
+        sampleInterfaceStringField: string;
+      }
 
-      @Query({ description: "sampleDateQueryDescription" })
-      sampleDateQuery(): Date {
-        return new Date(timestamp);
+      @ObjectType({ implements: SampleInterface })
+      class SampleType1 implements SampleInterface {
+        @Field()
+        sampleInterfaceStringField: string;
+        @Field({ description: "sampleType1StringFieldDescription" })
+        sampleType1StringField: string;
       }
 
-      @Query()
-      sampleServiceQuery(): string {
-        return this.sampleService.getSampleString();
+      @ObjectType({ implements: SampleInterface })
+      class SampleType2 implements SampleInterface {
+        @Field()
+        sampleInterfaceStringField: string;
+        @Field({ deprecationReason: "sampleType2StringFieldDeprecation" })
+        sampleType2StringField: string;
       }
 
-      @Query()
-      @UseMiddleware(async (_, next) => {
-        middlewareLogs.push("sampleMiddlewareBooleanQuery");
-        return next();
-      })
-      sampleMiddlewareBooleanQuery(): boolean {
-        return true;
+      @ObjectType()
+      class SampleType3 {
+        @Field()
+        sampleInterfaceStringField: string;
+        @Field()
+        sampleType3StringField: string;
       }
 
-      @Mutation()
-      sampleBooleanMutation(): boolean {
-        return true;
+      @InputType()
+      class SampleInput {
+        @Field()
+        @MinLength(10)
+        sampleInputStringField: string;
+        @Field()
+        sampleInputDefaultStringField: string = "sampleInputDefaultStringField";
       }
 
-      @Mutation()
-      sampleMutationWithInput(@Arg("input") input: SampleInput): boolean {
-        inputValue = input;
-        return true;
+      enum SampleNumberEnum {
+        OptionOne,
+        OptionTwo,
       }
+      registerEnumType(SampleNumberEnum, { name: "SampleNumberEnum" });
 
-      @Mutation()
-      @Authorized()
-      sampleAuthorizedMutation(): boolean {
-        return true;
+      enum SampleStringEnum {
+        OptionOne = "OptionOneString",
+        OptionTwo = "OptionTwoString",
       }
+      registerEnumType(SampleStringEnum, { name: "SampleStringEnum" });
 
-      @Query()
-      sampleInterfaceQuery(): SampleInterface {
-        const type1 = new SampleType1();
-        type1.sampleInterfaceStringField = "sampleInterfaceStringField";
-        type1.sampleType1StringField = "sampleType1StringField";
+      const SampleUnion = createUnionType({
+        types: () => [SampleType2, SampleType3],
+        name: "SampleUnion",
+        description: "SampleUnion description",
+      });
 
-        return type1;
-      }
+      const SampleResolveUnion = createUnionType({
+        types: () => [SampleType2, SampleType3],
+        name: "SampleResolveUnion",
+        resolveType: value => {
+          if ("sampleType2StringField" in value) {
+            return "SampleType2";
+          }
+          if ("sampleType3StringField" in value) {
+            return "SampleType3";
+          }
+          return;
+        },
+      });
 
-      @Query(returns => SampleUnion)
-      sampleUnionQuery(): typeof SampleUnion {
-        const type3 = new SampleType3();
-        type3.sampleInterfaceStringField = "sampleInterfaceStringField";
-        type3.sampleType3StringField = "sampleType3StringField";
+      @Service()
+      @Resolver()
+      class SampleResolver {
+        constructor(private readonly sampleService: SampleService) {}
 
-        return type3;
-      }
+        @Query({ description: "sampleDateQueryDescription" })
+        sampleDateQuery(): Date {
+          return new Date(timestamp);
+        }
 
-      @Query(returns => SampleResolveUnion)
-      sampleResolveUnionQuery(): typeof SampleResolveUnion {
-        return {
-          sampleInterfaceStringField: "sampleInterfaceStringField",
-          sampleType3StringField: "sampleType3StringField",
-        };
-      }
+        @Query()
+        sampleServiceQuery(): string {
+          return this.sampleService.getSampleString();
+        }
 
-      @Query(returns => SampleNumberEnum)
-      sampleNumberEnumQuery(
-        @Arg("numberEnum", type => SampleNumberEnum) numberEnum: SampleNumberEnum,
-      ): SampleNumberEnum {
-        enumValue = numberEnum;
-        return numberEnum;
-      }
+        @Query()
+        @UseMiddleware(async (_, next) => {
+          middlewareLogs.push("sampleMiddlewareBooleanQuery");
+          return next();
+        })
+        sampleMiddlewareBooleanQuery(): boolean {
+          return true;
+        }
 
-      @Query(returns => SampleStringEnum)
-      sampleStringEnumQuery(
-        @Arg("stringEnum", type => SampleStringEnum) stringEnum: SampleStringEnum,
-      ): SampleStringEnum {
-        enumValue = stringEnum;
-        return stringEnum;
-      }
+        @Mutation()
+        sampleBooleanMutation(): boolean {
+          return true;
+        }
 
-      @Subscription({
-        topics: "SAMPLE",
-      })
-      sampleSubscription(@Root() payload: number): number {
-        return payload;
-      }
-    }
-
-    pubSub = new PubSub();
-    ({ typeDefs, resolvers } = await buildTypeDefsAndResolvers({
-      resolvers: [SampleResolver],
-      authChecker: () => false,
-      pubSub,
-      container: Container,
-      orphanedTypes: [SampleType1],
-    }));
-    schema = makeExecutableSchema({
-      typeDefs,
-      resolvers,
-    });
-    const introspectionResult = await graphql(schema, getIntrospectionQuery());
-    schemaIntrospection = (introspectionResult.data as IntrospectionQuery).__schema;
-  });
+        @Mutation()
+        sampleMutationWithInput(@Arg("input") input: SampleInput): boolean {
+          inputValue = input;
+          return true;
+        }
 
-  it("should generate schema without errors", () => {
-    expect(schemaIntrospection).toBeDefined();
-  });
+        @Mutation()
+        @Authorized()
+        sampleAuthorizedMutation(): boolean {
+          return true;
+        }
 
-  describe("typeDefs", () => {
-    it("should generate typeDefs correctly", async () => {
-      expect(typeDefs).toBeDefined();
-    });
+        @Query()
+        sampleInterfaceQuery(): SampleInterface {
+          const type1 = new SampleType1();
+          type1.sampleInterfaceStringField = "sampleInterfaceStringField";
+          type1.sampleType1StringField = "sampleType1StringField";
 
-    it("should generate interface type", async () => {
-      const sampleInterface = schemaIntrospection.types.find(
-        it => it.name === "SampleInterface",
-      ) as IntrospectionInterfaceType;
-
-      expect(sampleInterface.kind).toBe(TypeKind.INTERFACE);
-      expect(sampleInterface.fields).toHaveLength(1);
-      expect(sampleInterface.fields[0].name).toBe("sampleInterfaceStringField");
-      expect(sampleInterface.possibleTypes).toHaveLength(2);
-      expect(sampleInterface.possibleTypes.map(it => it.name)).toContain("SampleType1");
-      expect(sampleInterface.possibleTypes.map(it => it.name)).toContain("SampleType2");
-    });
+          return type1;
+        }
 
-    it("should generate object types", async () => {
-      const sampleType1 = schemaIntrospection.types.find(
-        it => it.name === "SampleType1",
-      ) as IntrospectionObjectType;
-      const sampleType2 = schemaIntrospection.types.find(
-        it => it.name === "SampleType2",
-      ) as IntrospectionObjectType;
-      const sampleType1StringField = sampleType1.fields.find(
-        it => it.name === "sampleType1StringField",
-      )!;
-      const sampleType2StringField = sampleType2.fields.find(
-        it => it.name === "sampleType2StringField",
-      )!;
-
-      expect(sampleType1.kind).toBe(TypeKind.OBJECT);
-      expect(sampleType1.fields).toHaveLength(2);
-      expect(sampleType1StringField.description).toEqual("sampleType1StringFieldDescription");
-      expect(sampleType1.interfaces).toHaveLength(1);
-      expect(sampleType1.interfaces[0].name).toBe("SampleInterface");
-      expect(sampleType2StringField.deprecationReason).toBe("sampleType2StringFieldDeprecation");
-    });
+        @Query(returns => SampleUnion)
+        sampleUnionQuery(): typeof SampleUnion {
+          const type3 = new SampleType3();
+          type3.sampleInterfaceStringField = "sampleInterfaceStringField";
+          type3.sampleType3StringField = "sampleType3StringField";
 
-    it("should generate input type", async () => {
-      const sampleInput = schemaIntrospection.types.find(
-        it => it.name === "SampleInput",
-      ) as IntrospectionInputObjectType;
-      const sampleInputDefaultStringField = sampleInput.inputFields.find(
-        it => it.name === "sampleInputDefaultStringField",
-      )!;
-      const sampleInputDefaultStringFieldType = sampleInputDefaultStringField.type as IntrospectionNamedTypeRef;
-
-      expect(sampleInput.kind).toBe(TypeKind.INPUT_OBJECT);
-      expect(sampleInput.inputFields).toHaveLength(2);
-      expect(sampleInputDefaultStringFieldType).toEqual({
-        kind: "NON_NULL",
-        name: null,
-        ofType: {
-          kind: "SCALAR",
-          name: "String",
-          ofType: null,
-        },
+          return type3;
+        }
+
+        @Query(returns => SampleResolveUnion)
+        sampleResolveUnionQuery(): typeof SampleResolveUnion {
+          return {
+            sampleInterfaceStringField: "sampleInterfaceStringField",
+            sampleType3StringField: "sampleType3StringField",
+          };
+        }
+
+        @Query(returns => SampleNumberEnum)
+        sampleNumberEnumQuery(
+          @Arg("numberEnum", type => SampleNumberEnum) numberEnum: SampleNumberEnum,
+        ): SampleNumberEnum {
+          enumValue = numberEnum;
+          return numberEnum;
+        }
+
+        @Query(returns => SampleStringEnum)
+        sampleStringEnumQuery(
+          @Arg("stringEnum", type => SampleStringEnum) stringEnum: SampleStringEnum,
+        ): SampleStringEnum {
+          enumValue = stringEnum;
+          return stringEnum;
+        }
+
+        @Subscription({
+          topics: "SAMPLE",
+        })
+        sampleSubscription(@Root() payload: number): number {
+          return payload;
+        }
+      }
+
+      pubSub = new PubSub();
+      ({ typeDefs, resolvers } = await buildTypeDefsAndResolvers({
+        resolvers: [SampleResolver],
+        authChecker: () => false,
+        pubSub,
+        container: Container,
+        orphanedTypes: [SampleType1],
+      }));
+      schema = makeExecutableSchema({
+        typeDefs,
+        resolvers,
       });
-      expect(sampleInputDefaultStringField.defaultValue).toBe('"sampleInputDefaultStringField"');
+      const introspectionResult = await graphql(schema, getIntrospectionQuery());
+      schemaIntrospection = (introspectionResult.data as IntrospectionQuery).__schema;
     });
 
-    it("should generate enum types", async () => {
-      const sampleNumberEnum = schemaIntrospection.types.find(
-        it => it.name === "SampleNumberEnum",
-      ) as IntrospectionEnumType;
-      const sampleStringEnum = schemaIntrospection.types.find(
-        it => it.name === "SampleStringEnum",
-      ) as IntrospectionEnumType;
-
-      expect(sampleNumberEnum.kind).toBe(TypeKind.ENUM);
-      expect(sampleNumberEnum).toBeDefined();
-      expect(sampleNumberEnum.enumValues).toHaveLength(2);
-      expect(sampleStringEnum.enumValues).toHaveLength(2);
+    it("should generate schema without errors", () => {
+      expect(schemaIntrospection).toBeDefined();
     });
 
-    it("should generate union type", async () => {
-      const sampleUnion = schemaIntrospection.types.find(
-        it => it.name === "SampleUnion",
-      ) as IntrospectionUnionType;
+    describe("typeDefs", () => {
+      it("should generate typeDefs correctly", async () => {
+        expect(typeDefs).toBeDefined();
+      });
 
-      expect(sampleUnion.kind).toBe(TypeKind.UNION);
-      expect(sampleUnion.description).toBe("SampleUnion description");
-      expect(sampleUnion.possibleTypes).toHaveLength(2);
-      expect(sampleUnion.possibleTypes.map(it => it.name)).toContain("SampleType2");
-      expect(sampleUnion.possibleTypes.map(it => it.name)).toContain("SampleType3");
-    });
+      it("should generate interface type", async () => {
+        const sampleInterface = schemaIntrospection.types.find(
+          it => it.name === "SampleInterface",
+        ) as IntrospectionInterfaceType;
+
+        expect(sampleInterface.kind).toBe(TypeKind.INTERFACE);
+        expect(sampleInterface.fields).toHaveLength(1);
+        expect(sampleInterface.fields[0].name).toBe("sampleInterfaceStringField");
+        expect(sampleInterface.possibleTypes).toHaveLength(2);
+        expect(sampleInterface.possibleTypes.map(it => it.name)).toContain("SampleType1");
+        expect(sampleInterface.possibleTypes.map(it => it.name)).toContain("SampleType2");
+      });
 
-    it("should generate queries", async () => {
-      const queryType = schemaIntrospection.types.find(
-        it => it.name === schemaIntrospection.queryType.name,
-      ) as IntrospectionObjectType;
+      it("should generate object types", async () => {
+        const sampleType1 = schemaIntrospection.types.find(
+          it => it.name === "SampleType1",
+        ) as IntrospectionObjectType;
+        const sampleType2 = schemaIntrospection.types.find(
+          it => it.name === "SampleType2",
+        ) as IntrospectionObjectType;
+        const sampleType1StringField = sampleType1.fields.find(
+          it => it.name === "sampleType1StringField",
+        )!;
+        const sampleType2StringField = sampleType2.fields.find(
+          it => it.name === "sampleType2StringField",
+        )!;
+
+        expect(sampleType1.kind).toBe(TypeKind.OBJECT);
+        expect(sampleType1.fields).toHaveLength(2);
+        expect(sampleType1StringField.description).toEqual("sampleType1StringFieldDescription");
+        expect(sampleType1.interfaces).toHaveLength(1);
+        expect(sampleType1.interfaces[0].name).toBe("SampleInterface");
+        expect(sampleType2StringField.deprecationReason).toBe("sampleType2StringFieldDeprecation");
+      });
 
-      expect(queryType.fields).toHaveLength(8);
-    });
+      it("should generate input type", async () => {
+        const sampleInput = schemaIntrospection.types.find(
+          it => it.name === "SampleInput",
+        ) as IntrospectionInputObjectType;
+        const sampleInputDefaultStringField = sampleInput.inputFields.find(
+          it => it.name === "sampleInputDefaultStringField",
+        )!;
+        const sampleInputDefaultStringFieldType = sampleInputDefaultStringField.type as IntrospectionNamedTypeRef;
+
+        expect(sampleInput.kind).toBe(TypeKind.INPUT_OBJECT);
+        expect(sampleInput.inputFields).toHaveLength(2);
+        expect(sampleInputDefaultStringFieldType).toEqual({
+          kind: "NON_NULL",
+          name: null,
+          ofType: {
+            kind: "SCALAR",
+            name: "String",
+            ofType: null,
+          },
+        });
+        expect(sampleInputDefaultStringField.defaultValue).toBe('"sampleInputDefaultStringField"');
+      });
 
-    it("should generate mutations", async () => {
-      const mutationType = schemaIntrospection.types.find(
-        it => it.name === schemaIntrospection.mutationType!.name,
-      ) as IntrospectionObjectType;
+      it("should generate enum types", async () => {
+        const sampleNumberEnum = schemaIntrospection.types.find(
+          it => it.name === "SampleNumberEnum",
+        ) as IntrospectionEnumType;
+        const sampleStringEnum = schemaIntrospection.types.find(
+          it => it.name === "SampleStringEnum",
+        ) as IntrospectionEnumType;
+
+        expect(sampleNumberEnum.kind).toBe(TypeKind.ENUM);
+        expect(sampleNumberEnum).toBeDefined();
+        expect(sampleNumberEnum.enumValues).toHaveLength(2);
+        expect(sampleStringEnum.enumValues).toHaveLength(2);
+      });
 
-      expect(mutationType.fields).toHaveLength(3);
-    });
+      it("should generate union type", async () => {
+        const sampleUnion = schemaIntrospection.types.find(
+          it => it.name === "SampleUnion",
+        ) as IntrospectionUnionType;
 
-    it("should generate subscription", async () => {
-      const subscriptionType = schemaIntrospection.types.find(
-        it => it.name === schemaIntrospection.subscriptionType!.name,
-      ) as IntrospectionObjectType;
+        expect(sampleUnion.kind).toBe(TypeKind.UNION);
+        expect(sampleUnion.description).toBe("SampleUnion description");
+        expect(sampleUnion.possibleTypes).toHaveLength(2);
+        expect(sampleUnion.possibleTypes.map(it => it.name)).toContain("SampleType2");
+        expect(sampleUnion.possibleTypes.map(it => it.name)).toContain("SampleType3");
+      });
 
-      expect(subscriptionType.fields).toHaveLength(1);
-    });
+      it("should generate queries", async () => {
+        const queryType = schemaIntrospection.types.find(
+          it => it.name === schemaIntrospection.queryType.name,
+        ) as IntrospectionObjectType;
 
-    it("should emit Date scalar", async () => {
-      const dateScalar = schemaIntrospection.types.find(
-        it => it.name === "DateTime",
-      ) as IntrospectionScalarType;
+        expect(queryType.fields).toHaveLength(8);
+      });
 
-      expect(dateScalar.kind).toBe(TypeKind.SCALAR);
-    });
-  });
+      it("should generate mutations", async () => {
+        const mutationType = schemaIntrospection.types.find(
+          it => it.name === schemaIntrospection.mutationType!.name,
+        ) as IntrospectionObjectType;
 
-  describe("resolvers", () => {
-    it("should generate resolversMap without errors", async () => {
-      expect(resolvers).toBeDefined();
-    });
+        expect(mutationType.fields).toHaveLength(3);
+      });
 
-    it("should not emit `__isTypeOf` for root objects", async () => {
-      expect(resolvers.Query).not.toHaveProperty("__isTypeOf");
-      expect(resolvers.Mutation).not.toHaveProperty("__isTypeOf");
-      expect(resolvers.Subscription).not.toHaveProperty("__isTypeOf");
-    });
+      it("should generate subscription", async () => {
+        const subscriptionType = schemaIntrospection.types.find(
+          it => it.name === schemaIntrospection.subscriptionType!.name,
+        ) as IntrospectionObjectType;
 
-    it("should properly serialize Date scalar", async () => {
-      const document = gql`
-        query {
-          sampleDateQuery
-        }
-      `;
+        expect(subscriptionType.fields).toHaveLength(1);
+      });
 
-      const { data } = await execute(schema, document);
-      const parsedDate = new Date(data!.sampleDateQuery);
+      it("should emit Date scalar", async () => {
+        const dateScalar = schemaIntrospection.types.find(
+          it => it.name === "DateTime",
+        ) as IntrospectionScalarType;
 
-      expect(typeof data!.sampleDateQuery).toBe("string");
-      expect(parsedDate.getTime()).toEqual(timestamp);
+        expect(dateScalar.kind).toBe(TypeKind.SCALAR);
+      });
     });
 
-    it("should use container to resolve dependency", async () => {
-      const document = gql`
-        query {
-          sampleServiceQuery
-        }
-      `;
+    describe("resolvers", () => {
+      it("should generate resolversMap without errors", async () => {
+        expect(resolvers).toBeDefined();
+      });
 
-      const { data } = await execute(schema, document);
+      it("should not emit `__isTypeOf` for root objects", async () => {
+        expect(resolvers.Query).not.toHaveProperty("__isTypeOf");
+        expect(resolvers.Mutation).not.toHaveProperty("__isTypeOf");
+        expect(resolvers.Subscription).not.toHaveProperty("__isTypeOf");
+      });
 
-      expect(data!.sampleServiceQuery).toEqual("SampleString");
-    });
+      it("should properly serialize Date scalar", async () => {
+        const document = gql`
+          query {
+            sampleDateQuery
+          }
+        `;
 
-    it("should run resolver method middleware", async () => {
-      const document = gql`
-        query {
-          sampleMiddlewareBooleanQuery
-        }
-      `;
+        const { data } = await execute(schema, document);
+        const parsedDate = new Date(data!.sampleDateQuery);
 
-      const { data } = await execute(schema, document);
+        expect(typeof data!.sampleDateQuery).toBe("string");
+        expect(parsedDate.getTime()).toEqual(timestamp);
+      });
 
-      expect(data!.sampleMiddlewareBooleanQuery).toEqual(true);
-      expect(middlewareLogs).toHaveLength(1);
-      expect(middlewareLogs[0]).toEqual("sampleMiddlewareBooleanQuery");
-    });
+      it("should use container to resolve dependency", async () => {
+        const document = gql`
+          query {
+            sampleServiceQuery
+          }
+        `;
 
-    it("should allow for simple boolean mutation", async () => {
-      const document = gql`
-        mutation {
-          sampleBooleanMutation
-        }
-      `;
+        const { data } = await execute(schema, document);
 
-      const { data } = await execute(schema, document);
+        expect(data!.sampleServiceQuery).toEqual("SampleString");
+      });
 
-      expect(data!.sampleBooleanMutation).toBe(true);
-    });
+      it("should run resolver method middleware", async () => {
+        const document = gql`
+          query {
+            sampleMiddlewareBooleanQuery
+          }
+        `;
 
-    it("should properly transform input argument", async () => {
-      const document = gql`
-        mutation {
-          sampleMutationWithInput(input: { sampleInputStringField: "sampleInputStringField" })
-        }
-      `;
+        const { data } = await execute(schema, document);
 
-      const { data } = await execute(schema, document);
+        expect(data!.sampleMiddlewareBooleanQuery).toEqual(true);
+        expect(middlewareLogs).toHaveLength(1);
+        expect(middlewareLogs[0]).toEqual("sampleMiddlewareBooleanQuery");
+      });
 
-      expect(data!.sampleMutationWithInput).toBe(true);
-      expect(inputValue.constructor.name).toBe("SampleInput");
-      expect(inputValue.sampleInputStringField).toBe("sampleInputStringField");
-      expect(inputValue.sampleInputDefaultStringField).toBe("sampleInputDefaultStringField");
-    });
+      it("should allow for simple boolean mutation", async () => {
+        const document = gql`
+          mutation {
+            sampleBooleanMutation
+          }
+        `;
 
-    it("should validate the input", async () => {
-      const document = gql`
-        mutation {
-          sampleMutationWithInput(input: { sampleInputStringField: "short" })
-        }
-      `;
+        const { data } = await execute(schema, document);
 
-      const { errors } = await execute(schema, document);
+        expect(data!.sampleBooleanMutation).toBe(true);
+      });
 
-      expect(errors).toHaveLength(1);
-      expect(errors![0].message).toContain("Argument Validation Error");
-    });
+      it("should properly transform input argument", async () => {
+        const document = gql`
+          mutation {
+            sampleMutationWithInput(input: { sampleInputStringField: "sampleInputStringField" })
+          }
+        `;
 
-    it("should properly guard authorized resolver method", async () => {
-      const document = gql`
-        mutation {
-          sampleAuthorizedMutation
-        }
-      `;
+        const { data } = await execute(schema, document);
 
-      const { errors } = await execute(schema, document);
+        expect(data!.sampleMutationWithInput).toBe(true);
+        expect(inputValue.constructor.name).toBe("SampleInput");
+        expect(inputValue.sampleInputStringField).toBe("sampleInputStringField");
+        expect(inputValue.sampleInputDefaultStringField).toBe("sampleInputDefaultStringField");
+      });
 
-      expect(errors).toHaveLength(1);
-      expect(errors![0].message).toContain("Access denied");
-    });
+      it("should validate the input", async () => {
+        const document = gql`
+          mutation {
+            sampleMutationWithInput(input: { sampleInputStringField: "short" })
+          }
+        `;
 
-    it("should detect returned object type from interface", async () => {
-      const document = gql`
-        query {
-          sampleInterfaceQuery {
-            sampleInterfaceStringField
-            ... on SampleType1 {
-              sampleType1StringField
-            }
+        const { errors } = await execute(schema, document);
+
+        expect(errors).toHaveLength(1);
+        expect(errors![0].message).toContain("Argument Validation Error");
+      });
+
+      it("should properly guard authorized resolver method", async () => {
+        const document = gql`
+          mutation {
+            sampleAuthorizedMutation
           }
-        }
-      `;
+        `;
 
-      const { data } = await execute(schema, document);
+        const { errors } = await execute(schema, document);
 
-      expect(data!.sampleInterfaceQuery).toEqual({
-        sampleInterfaceStringField: "sampleInterfaceStringField",
-        sampleType1StringField: "sampleType1StringField",
+        expect(errors).toHaveLength(1);
+        expect(errors![0].message).toContain("Access denied");
       });
-    });
 
-    it("should detect returned object type from union", async () => {
-      const document = gql`
-        query {
-          sampleUnionQuery {
-            ... on SampleType3 {
+      it("should detect returned object type from interface", async () => {
+        const document = gql`
+          query {
+            sampleInterfaceQuery {
               sampleInterfaceStringField
-              sampleType3StringField
+              ... on SampleType1 {
+                sampleType1StringField
+              }
             }
           }
-        }
-      `;
+        `;
 
-      const { data } = await execute(schema, document);
+        const { data } = await execute(schema, document);
 
-      expect(data!.sampleUnionQuery).toEqual({
-        sampleInterfaceStringField: "sampleInterfaceStringField",
-        sampleType3StringField: "sampleType3StringField",
+        expect(data!.sampleInterfaceQuery).toEqual({
+          sampleInterfaceStringField: "sampleInterfaceStringField",
+          sampleType1StringField: "sampleType1StringField",
+        });
       });
-    });
 
-    it("should detect returned object type using resolveType from union", async () => {
-      const document = gql`
-        query {
-          sampleResolveUnionQuery {
-            ... on SampleType3 {
-              sampleInterfaceStringField
-              sampleType3StringField
+      it("should detect returned object type from union", async () => {
+        const document = gql`
+          query {
+            sampleUnionQuery {
+              ... on SampleType3 {
+                sampleInterfaceStringField
+                sampleType3StringField
+              }
             }
           }
-        }
-      `;
+        `;
 
-      const { data } = await execute(schema, document);
+        const { data } = await execute(schema, document);
 
-      expect(data!.sampleResolveUnionQuery).toEqual({
-        sampleInterfaceStringField: "sampleInterfaceStringField",
-        sampleType3StringField: "sampleType3StringField",
+        expect(data!.sampleUnionQuery).toEqual({
+          sampleInterfaceStringField: "sampleInterfaceStringField",
+          sampleType3StringField: "sampleType3StringField",
+        });
       });
-    });
 
-    it("should properly transform number enum argument", async () => {
-      const document = gql`
-        query {
-          sampleNumberEnumQuery(numberEnum: OptionOne)
-        }
-      `;
+      it("should detect returned object type using resolveType from union", async () => {
+        const document = gql`
+          query {
+            sampleResolveUnionQuery {
+              ... on SampleType3 {
+                sampleInterfaceStringField
+                sampleType3StringField
+              }
+            }
+          }
+        `;
 
-      const { data } = await execute(schema, document);
+        const { data } = await execute(schema, document);
 
-      expect(data!.sampleNumberEnumQuery).toBe("OptionOne");
-      expect(enumValue).toBe(0);
-    });
+        expect(data!.sampleResolveUnionQuery).toEqual({
+          sampleInterfaceStringField: "sampleInterfaceStringField",
+          sampleType3StringField: "sampleType3StringField",
+        });
+      });
 
-    it("should properly transform string enum argument", async () => {
-      const document = gql`
-        query {
-          sampleStringEnumQuery(stringEnum: OptionTwo)
-        }
-      `;
+      it("should properly transform number enum argument", async () => {
+        const document = gql`
+          query {
+            sampleNumberEnumQuery(numberEnum: OptionOne)
+          }
+        `;
+
+        const { data } = await execute(schema, document);
+
+        expect(data!.sampleNumberEnumQuery).toBe("OptionOne");
+        expect(enumValue).toBe(0);
+      });
+
+      it("should properly transform string enum argument", async () => {
+        const document = gql`
+          query {
+            sampleStringEnumQuery(stringEnum: OptionTwo)
+          }
+        `;
+
+        const { data } = await execute(schema, document);
+
+        expect(data!.sampleStringEnumQuery).toBe("OptionTwo");
+        expect(enumValue).toBe("OptionTwoString");
+      });
+
+      it("should properly run subscriptions", async () => {
+        const document = gql`
+          subscription {
+            sampleSubscription
+          }
+        `;
+        const payload = 5.4321;
+
+        const iterator = (await subscribe(schema, document)) as AsyncIterator<ExecutionResult>;
+        const firstValuePromise = iterator.next();
+        pubSub.publish("SAMPLE", payload);
+        const data = await firstValuePromise;
+
+        expect(data.value.data!.sampleSubscription).toBe(payload);
+      });
+
+      it("should generate simple resolvers function for queries and mutations", async () => {
+        expect((resolvers.Query as ResolverObject<any, any>).sampleDateQuery).toBeInstanceOf(
+          Function,
+        );
+        expect(
+          (resolvers.Mutation as ResolverObject<any, any>).sampleBooleanMutation,
+        ).toBeInstanceOf(Function);
+      });
 
-      const { data } = await execute(schema, document);
+      it("should generate resolvers object for subscriptions", async () => {
+        const sampleSubscription = (resolvers.Subscription as ResolverObject<any, any>)
+          .sampleSubscription as ResolverOptions<any, any>;
 
-      expect(data!.sampleStringEnumQuery).toBe("OptionTwo");
-      expect(enumValue).toBe("OptionTwoString");
+        expect(sampleSubscription.resolve).toBeInstanceOf(Function);
+        expect(sampleSubscription.subscribe).toBeInstanceOf(Function);
+      });
     });
+  });
 
-    it("should properly run subscriptions", async () => {
-      const document = gql`
-        subscription {
-          sampleSubscription
+  describe("buildTypeDefsAndResolversSync", () => {
+    let typeDefs: string;
+    let resolvers: ResolversMap;
+    let schemaIntrospection: IntrospectionSchema;
+    let schema: GraphQLSchema;
+
+    beforeAll(async () => {
+      getMetadataStorage().clear();
+
+      @ObjectType()
+      class SampleType {
+        @Field()
+        sampleInterfaceStringField: string;
+        @Field({ description: "sampleTypeStringFieldDescription" })
+        sampleTypeStringField: string;
+      }
+
+      @Resolver()
+      class SampleResolver {
+        @Query()
+        sampleBooleanQuery(): boolean {
+          return true;
         }
-      `;
-      const payload = 5.4321;
+      }
 
-      const iterator = (await subscribe(schema, document)) as AsyncIterator<ExecutionResult>;
-      const firstValuePromise = iterator.next();
-      pubSub.publish("SAMPLE", payload);
-      const data = await firstValuePromise;
+      ({ typeDefs, resolvers } = buildTypeDefsAndResolversSync({
+        resolvers: [SampleResolver],
+        authChecker: () => false,
+        orphanedTypes: [SampleType],
+      }));
+      schema = makeExecutableSchema({
+        typeDefs,
+        resolvers,
+      });
+      const introspectionResult = await graphql(schema, getIntrospectionQuery());
+      schemaIntrospection = (introspectionResult.data as IntrospectionQuery).__schema;
+    });
 
-      expect(data.value.data!.sampleSubscription).toBe(payload);
+    it("should generate schema without errors", () => {
+      expect(schemaIntrospection).toBeDefined();
     });
 
-    it("should generate simple resolvers function for queries and mutations", async () => {
-      expect((resolvers.Query as ResolverObject<any, any>).sampleDateQuery).toBeInstanceOf(
-        Function,
-      );
-      expect((resolvers.Mutation as ResolverObject<any, any>).sampleBooleanMutation).toBeInstanceOf(
-        Function,
-      );
+    describe("typeDefs", () => {
+      it("should generate typeDefs correctly", async () => {
+        expect(typeDefs).toBeDefined();
+      });
+
+      it("should generate object types", async () => {
+        const sampleType = schemaIntrospection.types.find(
+          it => it.name === "SampleType",
+        ) as IntrospectionObjectType;
+        const sampleTypeStringField = sampleType.fields.find(
+          it => it.name === "sampleTypeStringField",
+        )!;
+
+        expect(sampleType.kind).toBe(TypeKind.OBJECT);
+        expect(sampleType.fields).toHaveLength(2);
+        expect(sampleTypeStringField.description).toEqual("sampleTypeStringFieldDescription");
+        expect(sampleType.interfaces).toHaveLength(0);
+      });
     });
 
-    it("should generate resolvers object for subscriptions", async () => {
-      const sampleSubscription = (resolvers.Subscription as ResolverObject<any, any>)
-        .sampleSubscription as ResolverOptions<any, any>;
+    describe("resolvers", () => {
+      it("should generate resolversMap without errors", async () => {
+        expect(resolvers).toBeDefined();
+      });
 
-      expect(sampleSubscription.resolve).toBeInstanceOf(Function);
-      expect(sampleSubscription.subscribe).toBeInstanceOf(Function);
+      it("should allow for simple boolean query", async () => {
+        const document = gql`
+          query {
+            sampleBooleanQuery
+          }
+        `;
+
+        const { data, errors } = await execute(schema, document);
+
+        expect(errors).toBeUndefined();
+        expect(data!.sampleBooleanQuery).toBe(true);
+      });
     });
   });
 });