├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── documentation-issue-or-request.md │ └── feature_request.md ├── configs │ └── changelog.json ├── dependabot.yml └── workflows │ ├── check.yml │ ├── codeql.yml │ ├── license.yml │ ├── release.yml │ ├── sponsor.yml │ └── website.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── .markdownlint.json ├── .markdownlintignore ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .shellcheckrc ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── benchmarks ├── .eslintrc ├── array │ ├── graphql-js │ │ ├── async.ts │ │ └── standard.ts │ ├── results.txt │ ├── run.ts │ └── type-graphql │ │ ├── async-field-resolvers.ts │ │ ├── simple-resolvers.ts │ │ ├── standard.ts │ │ ├── sync-field-resolvers.ts │ │ ├── sync-getters.ts │ │ └── with-global-middleware.ts ├── simple │ ├── graphql-js.ts │ ├── results.txt │ ├── run.ts │ └── type-graphql.ts └── tsconfig.json ├── cspell.json ├── docs ├── README.md ├── authorization.md ├── aws-lambda.md ├── azure-functions.md ├── bootstrap.md ├── browser-usage.md ├── complexity.md ├── custom-decorators.md ├── dependency-injection.md ├── directives.md ├── emit-schema.md ├── enums.md ├── esm.md ├── examples.md ├── extensions.md ├── faq.md ├── generic-types.md ├── getting-started.md ├── inheritance.md ├── installation.md ├── interfaces.md ├── introduction.md ├── middlewares.md ├── migration-guide.md ├── nestjs.md ├── performance.md ├── prisma.md ├── resolvers.md ├── scalars.md ├── subscriptions.md ├── types-and-fields.md ├── unions.md └── validation.md ├── examples ├── .eslintrc ├── README.md ├── apollo-cache │ ├── cache-control.ts │ ├── examples.graphql │ ├── helpers │ │ ├── RequireAtLeastOne.d.ts │ │ └── getTime.ts │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ └── schema.graphql ├── apollo-federation-2 │ ├── accounts │ │ ├── data.ts │ │ ├── index.ts │ │ ├── resolver.ts │ │ ├── user.reference.ts │ │ └── user.ts │ ├── examples.graphql │ ├── helpers │ │ └── buildFederatedSchema.ts │ ├── index.ts │ ├── inventory │ │ ├── data.ts │ │ ├── index.ts │ │ ├── product.reference.ts │ │ ├── product.ts │ │ └── resolver.ts │ ├── products │ │ ├── data.ts │ │ ├── dining.ts │ │ ├── index.ts │ │ ├── product.reference.ts │ │ ├── product.ts │ │ ├── resolver.ts │ │ └── seating.ts │ ├── reviews │ │ ├── index.ts │ │ ├── product │ │ │ ├── product.ts │ │ │ └── resolver.ts │ │ ├── review │ │ │ ├── data.ts │ │ │ ├── resolver.ts │ │ │ └── review.ts │ │ └── user │ │ │ ├── resolver.ts │ │ │ └── user.ts │ └── schema.graphql ├── apollo-federation │ ├── accounts │ │ ├── data.ts │ │ ├── index.ts │ │ ├── resolver.ts │ │ ├── user.reference.ts │ │ └── user.ts │ ├── examples.graphql │ ├── helpers │ │ ├── buildFederatedSchema.ts │ │ └── index.ts │ ├── index.ts │ ├── inventory │ │ ├── data.ts │ │ ├── index.ts │ │ ├── product.reference.ts │ │ ├── product.ts │ │ └── resolver.ts │ ├── products │ │ ├── data.ts │ │ ├── index.ts │ │ ├── product.reference.ts │ │ ├── product.ts │ │ └── resolver.ts │ ├── reviews │ │ ├── index.ts │ │ ├── product │ │ │ ├── index.ts │ │ │ ├── product.ts │ │ │ └── resolver.ts │ │ ├── review │ │ │ ├── data.ts │ │ │ ├── index.ts │ │ │ ├── resolver.ts │ │ │ └── review.ts │ │ └── user │ │ │ ├── index.ts │ │ │ ├── resolver.ts │ │ │ └── user.ts │ └── schema.graphql ├── authorization │ ├── auth-checker.ts │ ├── context.type.ts │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ ├── schema.graphql │ └── user.type.ts ├── automatic-validation │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ ├── recipes.arguments.ts │ └── schema.graphql ├── custom-validation │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ ├── recipes.arguments.ts │ └── schema.graphql ├── enums-and-unions │ ├── cook.data.ts │ ├── cook.type.ts │ ├── difficulty.enum.ts │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.type.ts │ ├── resolver.ts │ ├── schema.graphql │ └── search-result.union.ts ├── extensions │ ├── context.type.ts │ ├── examples.graphql │ ├── helpers │ │ └── config.extractors.ts │ ├── index.ts │ ├── log-message.decorator.ts │ ├── logger.middleware.ts │ ├── logger.service.ts │ ├── recipe.data.ts │ ├── recipe.type.ts │ ├── resolver.ts │ ├── schema.graphql │ └── user.type.ts ├── generic-types │ ├── examples.graphql │ ├── index.ts │ ├── paginated-response.type.ts │ ├── recipe.data.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ └── schema.graphql ├── graphql-scalars │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ └── schema.graphql ├── interfaces-inheritance │ ├── employee │ │ ├── employee.input.ts │ │ ├── employee.type.ts │ │ └── index.ts │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── person │ │ ├── index.ts │ │ ├── person.input.ts │ │ ├── person.interface.ts │ │ └── person.type.ts │ ├── resolver.ts │ ├── resource │ │ ├── index.ts │ │ └── resource.interface.ts │ ├── schema.graphql │ └── student │ │ ├── index.ts │ │ ├── student.input.ts │ │ └── student.type.ts ├── middlewares-custom-decorators │ ├── context.type.ts │ ├── decorators │ │ ├── current-user.ts │ │ ├── index.ts │ │ ├── random-id-arg.ts │ │ └── validate-args.ts │ ├── examples.graphql │ ├── index.ts │ ├── logger.ts │ ├── middlewares │ │ ├── error-logger.ts │ │ ├── index.ts │ │ ├── log-access.ts │ │ ├── number-interceptor.ts │ │ └── resolve-time.ts │ ├── recipe │ │ ├── index.ts │ │ ├── recipe.args.ts │ │ ├── recipe.data.ts │ │ ├── recipe.resolver.ts │ │ └── recipe.type.ts │ ├── schema.graphql │ └── user.type.ts ├── mikro-orm │ ├── .env.example │ ├── context.type.ts │ ├── entities │ │ ├── index.ts │ │ ├── rating.ts │ │ ├── recipe.ts │ │ └── user.ts │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── resolvers │ │ ├── index.ts │ │ ├── rating.resolver.ts │ │ ├── recipe.resolver.ts │ │ └── types │ │ │ ├── index.ts │ │ │ ├── rating.input.ts │ │ │ └── recipe.input.ts │ └── schema.graphql ├── mixin-classes │ ├── examples.graphql │ ├── index.ts │ ├── inputs │ │ ├── amend.user.input.ts │ │ ├── create.user.input.ts │ │ └── index.ts │ ├── mixins │ │ ├── index.ts │ │ ├── with.id.ts │ │ └── with.password.ts │ ├── resolver.ts │ ├── schema.graphql │ └── types │ │ ├── index.ts │ │ ├── user.details.ts │ │ └── user.ts ├── query-complexity │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ └── schema.graphql ├── redis-subscriptions │ ├── .env.example │ ├── comment.input.ts │ ├── comment.type.ts │ ├── examples.graphql │ ├── index.ts │ ├── pubsub.ts │ ├── recipe.data.ts │ ├── recipe.resolver.args.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ └── schema.graphql ├── resolvers-inheritance │ ├── examples.graphql │ ├── index.ts │ ├── person │ │ ├── index.ts │ │ ├── person.resolver.ts │ │ ├── person.role.ts │ │ └── person.type.ts │ ├── recipe │ │ ├── index.ts │ │ ├── recipe.resolver.ts │ │ └── recipe.type.ts │ ├── resource │ │ ├── index.ts │ │ ├── resource.args.ts │ │ ├── resource.resolver.ts │ │ ├── resource.service.factory.ts │ │ ├── resource.service.ts │ │ └── resource.ts │ └── schema.graphql ├── simple-subscriptions │ ├── examples.graphql │ ├── index.ts │ ├── notification.resolver.ts │ ├── notification.type.ts │ ├── pubsub.ts │ └── schema.graphql ├── simple-usage │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.type.ts │ └── schema.graphql ├── tsconfig.json ├── tsyringe │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.service.ts │ ├── recipe.type.ts │ └── schema.graphql ├── typegoose │ ├── .env.example │ ├── context.type.ts │ ├── entities │ │ ├── index.ts │ │ ├── rating.ts │ │ ├── recipe.ts │ │ └── user.ts │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── object-id.scalar.ts │ ├── resolvers │ │ ├── index.ts │ │ ├── rating.resolver.ts │ │ ├── recipe.resolver.ts │ │ └── types │ │ │ ├── index.ts │ │ │ ├── rating.input.ts │ │ │ └── recipe.input.ts │ ├── schema.graphql │ ├── typegoose.middleware.ts │ └── types.ts ├── typeorm-basic-usage │ ├── .env.example │ ├── context.type.ts │ ├── datasource.ts │ ├── entities │ │ ├── index.ts │ │ ├── rating.ts │ │ ├── recipe.ts │ │ └── user.ts │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── resolvers │ │ ├── index.ts │ │ ├── rating.resolver.ts │ │ ├── recipe.resolver.ts │ │ └── types │ │ │ ├── index.ts │ │ │ ├── rating.input.ts │ │ │ └── recipe.input.ts │ └── schema.graphql ├── typeorm-lazy-relations │ ├── .env.example │ ├── context.type.ts │ ├── datasource.ts │ ├── entities │ │ ├── index.ts │ │ ├── rating.ts │ │ ├── recipe.ts │ │ └── user.ts │ ├── examples.graphql │ ├── helpers.ts │ ├── index.ts │ ├── resolvers │ │ ├── index.ts │ │ ├── recipe.resolver.ts │ │ └── types │ │ │ ├── index.ts │ │ │ ├── rating.input.ts │ │ │ └── recipe.input.ts │ └── schema.graphql ├── using-container │ ├── examples.graphql │ ├── index.ts │ ├── recipe.data.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.service.ts │ ├── recipe.type.ts │ └── schema.graphql └── using-scoped-container │ ├── context.type.ts │ ├── examples.graphql │ ├── index.ts │ ├── logger.ts │ ├── recipe │ ├── recipe.data.ts │ ├── recipe.input.ts │ ├── recipe.resolver.ts │ ├── recipe.service.ts │ └── recipe.type.ts │ └── schema.graphql ├── images ├── admin-remix.png ├── alka.png ├── betwinner.svg ├── blue_receipt.gif ├── buzzoid.svg ├── c19.png ├── casinodeps.svg ├── chums.svg ├── ecad.png ├── ecad_new.png ├── famety.png ├── fansoria.png ├── flatirons.png ├── github-sponsors.svg ├── gorrion.png ├── graming.svg ├── ig-comment.png ├── insfollowpro.png ├── instinctools.svg ├── intexsoft.jpg ├── kaszinomagyar.svg ├── leofame.png ├── lifex.png ├── live-graphics-system.png ├── logo-buzzv.png ├── logo.png ├── lovd.png ├── mr-yum.png ├── non-gamstop-casinos.svg ├── non-stop-casino.png ├── nongamstopbets.png ├── nove_casino.svg ├── play_fortune.png ├── polis.png ├── pwmlogo.png ├── qulix.png ├── rubydroid.svg ├── sidesmedia.png ├── slon.png ├── social_followers.png ├── socialmention.webp ├── socialwick-logo.png ├── strands-hint-logo.svg ├── swiss-mentor.png ├── tiktok-expert.jpg ├── tokmax.svg ├── twicsy.svg ├── use-viral-logo.webp ├── vps-server.png ├── wordhint.jpg └── zahranicnicasino.svg ├── jest.config.ts ├── package-lock.json ├── package.json ├── scripts ├── .eslintrc ├── markdown.ts ├── package.json.ts ├── tsconfig.json └── version.ts ├── sponsorkit.config.mts ├── src ├── @types │ └── Reflect.d.ts ├── decorators │ ├── Arg.ts │ ├── Args.ts │ ├── ArgsType.ts │ ├── Authorized.ts │ ├── Ctx.ts │ ├── Directive.ts │ ├── Extensions.ts │ ├── Field.ts │ ├── FieldResolver.ts │ ├── Info.ts │ ├── InputType.ts │ ├── InterfaceType.ts │ ├── Mutation.ts │ ├── ObjectType.ts │ ├── Query.ts │ ├── Resolver.ts │ ├── Root.ts │ ├── Subscription.ts │ ├── UseMiddleware.ts │ ├── createMethodMiddlewareDecorator.ts │ ├── createParameterDecorator.ts │ ├── createResolverClassMiddlewareDecorator.ts │ ├── enums.ts │ ├── index.ts │ ├── types.ts │ └── unions.ts ├── errors │ ├── CannotDetermineGraphQLTypeError.ts │ ├── ConflictingDefaultValuesError.ts │ ├── GeneratingSchemaError.ts │ ├── InterfaceResolveTypeError.ts │ ├── InvalidDirectiveError.ts │ ├── MissingPubSubError.ts │ ├── MissingSubscriptionTopicsError.ts │ ├── NoExplicitTypeError.ts │ ├── ReflectMetadataMissingError.ts │ ├── SymbolKeysNotSupportedError.ts │ ├── UnionResolveTypeError.ts │ ├── UnmetGraphQLPeerDependencyError.ts │ ├── WrongNullableListOptionError.ts │ ├── graphql │ │ ├── ArgumentValidationError.ts │ │ ├── AuthenticationError.ts │ │ ├── AuthorizationError.ts │ │ └── index.ts │ └── index.ts ├── helpers │ ├── auth-middleware.ts │ ├── decorators.ts │ ├── filesystem.ts │ ├── findType.ts │ ├── isThrowing.ts │ ├── params.ts │ ├── resolver-metadata.ts │ ├── returnTypes.ts │ ├── types.ts │ └── utils.ts ├── index.ts ├── metadata │ ├── definitions │ │ ├── authorized-metadata.ts │ │ ├── class-metadata.ts │ │ ├── directive-metadata.ts │ │ ├── enum-metadata.ts │ │ ├── extensions-metadata.ts │ │ ├── field-metadata.ts │ │ ├── index.ts │ │ ├── interface-class-metadata.ts │ │ ├── middleware-metadata.ts │ │ ├── object-class-metadata.ts │ │ ├── param-metadata.ts │ │ ├── resolver-metadata.ts │ │ └── union-metadata.ts │ ├── getMetadataStorage.ts │ ├── index.ts │ ├── metadata-storage.ts │ └── utils.ts ├── resolvers │ ├── convert-args.ts │ ├── create.ts │ ├── helpers.ts │ └── validate-arg.ts ├── scalars │ ├── aliases.ts │ └── index.ts ├── schema │ ├── build-context.ts │ ├── definition-node.ts │ ├── schema-generator.ts │ └── utils.ts ├── shim.ts ├── typings │ ├── Complexity.ts │ ├── ResolverInterface.ts │ ├── SubscribeResolverData.ts │ ├── SubscriptionHandlerData.ts │ ├── TypeResolver.ts │ ├── ValidatorFn.ts │ ├── auth-checker.ts │ ├── index.ts │ ├── legacy-decorators.ts │ ├── middleware.ts │ ├── resolver-data.ts │ ├── resolvers-map.ts │ ├── subscriptions.ts │ └── utils │ │ ├── ClassType.ts │ │ ├── Constructor.ts │ │ ├── Except.ts │ │ ├── IsEqual.ts │ │ ├── Maybe.ts │ │ ├── MaybePromise.ts │ │ ├── MergeExclusive.ts │ │ ├── NonEmptyArray.ts │ │ ├── SetRequired.ts │ │ ├── Simplify.ts │ │ └── index.ts └── utils │ ├── buildSchema.ts │ ├── buildTypeDefsAndResolvers.ts │ ├── container.ts │ ├── createResolversMap.ts │ ├── emitSchemaDefinitionFile.ts │ ├── graphql-version.ts │ ├── index.ts │ └── isPromiseLike.ts ├── tests ├── .eslintrc ├── functional │ ├── authorization.ts │ ├── circular-refs.ts │ ├── default-nullable.ts │ ├── default-values.ts │ ├── deprecation.ts │ ├── description.ts │ ├── directives.ts │ ├── emit-schema-sdl.ts │ ├── enums.ts │ ├── errors │ │ └── metadata-polyfill.ts │ ├── extensions.ts │ ├── fields.ts │ ├── generic-types.ts │ ├── interface-resolvers-args.ts │ ├── interfaces-and-inheritance.ts │ ├── ioc-container.ts │ ├── manual-decorators.ts │ ├── metadata-storage.ts │ ├── middlewares.ts │ ├── nested-interface-inheritance.ts │ ├── peer-dependency.ts │ ├── query-complexity.ts │ ├── resolvers.ts │ ├── scalars.ts │ ├── simple-resolvers.ts │ ├── subscriptions.ts │ ├── typedefs-resolvers.ts │ ├── unions.ts │ └── validation.ts ├── helpers │ ├── circular-refs │ │ ├── good │ │ │ ├── CircularRef1.ts │ │ │ └── CircularRef2.ts │ │ └── wrong │ │ │ ├── CircularRef1.ts │ │ │ └── CircularRef2.ts │ ├── customScalar.ts │ ├── directives │ │ ├── TestDirective.ts │ │ └── assertValidDirective.ts │ ├── expectToThrow.ts │ ├── getInnerFieldType.ts │ ├── getSampleObjectFieldType.ts │ ├── getSchemaInfo.ts │ ├── getTypeField.ts │ └── sleep.ts └── tsconfig.json ├── tsconfig.cjs.json ├── tsconfig.esm.json ├── tsconfig.json ├── tsconfig.typings.json └── website ├── .gitignore ├── blog ├── 2018-03-25-medium-article.md ├── 2020-08-19-devto-article.md └── assets │ └── logo_mini.png ├── core └── Footer.js ├── i18n └── en.json ├── package-lock.json ├── package.json ├── pages ├── en │ ├── help.js │ ├── index.js │ ├── users.js │ └── versions.js └── snippets │ ├── object-type.md │ ├── testability.md │ ├── typeorm.md │ └── validation.md ├── sidebars.json ├── siteConfig.js ├── static ├── css │ ├── custom.css │ └── prism-theme.css └── img │ ├── GraphQL_Logo.svg │ ├── admin-remix.png │ ├── author.jpg │ ├── betwinner.svg │ ├── blue_receipt.gif │ ├── buzzoid.svg │ ├── c19.png │ ├── casinodeps.svg │ ├── chums.svg │ ├── ecad.png │ ├── ecad_new.png │ ├── famety.png │ ├── fansoria.png │ ├── favicon.png │ ├── favicon │ └── favicon.ico │ ├── flatirons.png │ ├── github-sponsors.svg │ ├── gorrion.png │ ├── graming.svg │ ├── ig-comment.png │ ├── instinctools.svg │ ├── intexsoft.jpg │ ├── kaszinomagyar.svg │ ├── leofame.png │ ├── lifex.svg │ ├── live-graphics-system.png │ ├── logo-buzzv.png │ ├── logo.png │ ├── lovd.png │ ├── non-gamstop-casinos.svg │ ├── non-stop-casino.png │ ├── nongamstopbets.png │ ├── nove_casino.svg │ ├── play_fortune.png │ ├── pwmlogo.png │ ├── qulix.png │ ├── rubydroid.svg │ ├── sidesmedia.png │ ├── slon.png │ ├── social_followers.png │ ├── socialmention.webp │ ├── socialwick-logo.png │ ├── strands-hint-logo.svg │ ├── swiss-mentor.png │ ├── tiktok-expert.jpg │ ├── tokmax.svg │ ├── tools.svg │ ├── ts-logo.png │ ├── twicsy.svg │ ├── use-viral-logo.webp │ ├── wordhint.jpg │ └── zahranicnicasino.svg ├── versioned_docs ├── version-0.16.0 │ ├── authorization.md │ ├── bootstrap.md │ ├── browser-usage.md │ ├── complexity.md │ ├── dependency-injection.md │ ├── emit-schema.md │ ├── enums.md │ ├── examples.md │ ├── faq.md │ ├── getting-started.md │ ├── interfaces-and-inheritance.md │ ├── introduction.md │ ├── middlewares.md │ ├── resolvers.md │ ├── scalars.md │ ├── subscriptions.md │ ├── types-and-fields.md │ ├── unions.md │ └── validation.md ├── version-0.17.0 │ ├── authorization.md │ ├── bootstrap.md │ ├── browser-usage.md │ ├── complexity.md │ ├── dependency-injection.md │ ├── emit-schema.md │ ├── enums.md │ ├── examples.md │ ├── faq.md │ ├── generic-types.md │ ├── getting-started.md │ ├── inheritance.md │ ├── installation.md │ ├── interfaces.md │ ├── introduction.md │ ├── middlewares.md │ ├── resolvers.md │ ├── scalars.md │ ├── subscriptions.md │ ├── types-and-fields.md │ ├── unions.md │ └── validation.md ├── version-0.17.1 │ ├── authorization.md │ ├── bootstrap.md │ ├── browser-usage.md │ ├── complexity.md │ ├── dependency-injection.md │ ├── emit-schema.md │ ├── enums.md │ ├── examples.md │ ├── faq.md │ ├── generic-types.md │ ├── getting-started.md │ ├── inheritance.md │ ├── installation.md │ ├── interfaces.md │ ├── introduction.md │ ├── middlewares.md │ ├── resolvers.md │ ├── scalars.md │ ├── subscriptions.md │ ├── types-and-fields.md │ ├── unions.md │ └── validation.md ├── version-0.17.2 │ ├── examples.md │ ├── getting-started.md │ ├── interfaces.md │ ├── introduction.md │ ├── types-and-fields.md │ └── unions.md ├── version-0.17.4 │ ├── custom-decorators.md │ ├── examples.md │ ├── interfaces.md │ ├── middlewares.md │ ├── subscriptions.md │ └── unions.md ├── version-0.17.5 │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── enums.md │ ├── examples.md │ ├── faq.md │ ├── generic-types.md │ ├── interfaces.md │ ├── middlewares.md │ ├── subscriptions.md │ ├── types-and-fields.md │ └── unions.md ├── version-0.17.6 │ └── custom-decorators.md ├── version-1.0.0 │ ├── bootstrap.md │ ├── browser-usage.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── directives.md │ ├── emit-schema.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── getting-started.md │ ├── installation.md │ ├── interfaces.md │ ├── middlewares.md │ ├── nestjs.md │ ├── performance.md │ ├── prisma.md │ ├── resolvers.md │ ├── scalars.md │ ├── subscriptions.md │ ├── types-and-fields.md │ ├── unions.md │ └── validation.md ├── version-1.1.0 │ ├── enums.md │ ├── examples.md │ └── validation.md ├── version-1.1.1 │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── interfaces.md │ ├── middlewares.md │ ├── performance.md │ ├── resolvers.md │ ├── subscriptions.md │ ├── unions.md │ └── validation.md ├── version-1.2.0-rc.1 │ ├── authorization.md │ ├── bootstrap.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── directives.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── interfaces.md │ ├── middlewares.md │ ├── performance.md │ ├── resolvers.md │ ├── subscriptions.md │ ├── unions.md │ └── validation.md ├── version-2.0.0-beta.3 │ ├── authorization.md │ ├── bootstrap.md │ ├── browser-usage.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── directives.md │ ├── emit-schema.md │ ├── enums.md │ ├── esm.md │ ├── examples.md │ ├── extensions.md │ ├── faq.md │ ├── generic-types.md │ ├── getting-started.md │ ├── inheritance.md │ ├── installation.md │ ├── interfaces.md │ ├── introduction.md │ ├── middlewares.md │ ├── nestjs.md │ ├── performance.md │ ├── prisma.md │ ├── resolvers.md │ ├── scalars.md │ ├── subscriptions.md │ ├── types-and-fields.md │ ├── unions.md │ └── validation.md ├── version-2.0.0-beta.4 │ ├── authorization.md │ ├── aws-lambda.md │ ├── browser-usage.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── directives.md │ ├── emit-schema.md │ ├── esm.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── inheritance.md │ ├── interfaces.md │ ├── middlewares.md │ ├── migration-guide.md │ ├── resolvers.md │ ├── subscriptions.md │ ├── unions.md │ └── validation.md ├── version-2.0.0-beta.6 │ ├── authorization.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── inheritance.md │ ├── interfaces.md │ ├── middlewares.md │ ├── resolvers.md │ ├── subscriptions.md │ ├── unions.md │ └── validation.md ├── version-2.0.0-rc.1 │ ├── authorization.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── inheritance.md │ ├── installation.md │ ├── interfaces.md │ ├── middlewares.md │ ├── migration-guide.md │ ├── resolvers.md │ ├── subscriptions.md │ ├── unions.md │ └── validation.md └── version-2.0.0-rc.2 │ ├── authorization.md │ ├── azure-functions.md │ ├── browser-usage.md │ ├── complexity.md │ ├── custom-decorators.md │ ├── dependency-injection.md │ ├── examples.md │ ├── extensions.md │ ├── generic-types.md │ ├── inheritance.md │ ├── interfaces.md │ ├── middlewares.md │ ├── resolvers.md │ ├── subscriptions.md │ ├── unions.md │ └── validation.md ├── versioned_sidebars ├── version-0.16.0-sidebars.json ├── version-0.17.0-sidebars.json ├── version-0.17.4-sidebars.json ├── version-1.0.0-sidebars.json ├── version-2.0.0-beta.3-sidebars.json ├── version-2.0.0-beta.4-sidebars.json └── version-2.0.0-rc.2-sidebars.json └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/build/ 2 | **/dist/ 3 | **/coverage/ 4 | **/node_modules/ 5 | jest.config.ts 6 | sponsorkit.config.mts 7 | 8 | # FIXME: Remove 9 | website/ 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @MichalLytek 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: typegraphql 2 | open_collective: typegraphql 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Question or help request 4 | url: https://github.com/MichalLytek/type-graphql/discussions 5 | about: Github Discussions is the place to ask a question or discuss with other community members 6 | - name: Prisma 2 integration 7 | url: https://github.com/MichalLytek/typegraphql-prisma 8 | about: All problems or a questions about `typegraphql-prisma` package should be placed in the separate repository 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-issue-or-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation issue or request 3 | about: There's something wrong in docs or something is missing 4 | --- 5 | 6 | **Describe the issue** 7 | A clear and concise description of what is wrong or what feature is missing. 8 | You may ask here for guides, e.g. "How to run TypeGraphQL on AWS Lamda?" if nobody helped you on Github Discussions or StackOverflow. 9 | 10 | **Are you able to make a PR that fix this?** 11 | If you can, it would be great if you create a pull request that fixes the docs, fills the gap with new chapter or new code example. 12 | 13 | **Additional context** 14 | Add any other context about the problem here. 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: You want to suggest an idea for this project 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 8 | 9 | **Describe the solution you'd like** 10 | A clear and concise description of what you want to happen. 11 | You can also propose how the new API should looks like. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/configs/changelog.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "title": "## 🚀 Enhancements", 5 | "labels": ["Enhancement 🆕"] 6 | }, 7 | { 8 | "title": "## 🐛 Fixes", 9 | "labels": ["Bugfix 🐛 🔨"] 10 | }, 11 | { 12 | "title": "## 🧪 Tests", 13 | "labels": ["Test 🧪"] 14 | }, 15 | { 16 | "title": "## 📦 Dependencies", 17 | "labels": ["Dependencies 📦"] 18 | }, 19 | { 20 | "title": "## 📚 Documentation", 21 | "labels": ["Documentation 📖"] 22 | }, 23 | { 24 | "title": "## 🏠 Internal", 25 | "labels": ["Chore 🔨", "Internal 🏠"] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | versioning-strategy: increase 6 | schedule: 7 | interval: "weekly" 8 | ignore: 9 | - dependency-name: "*" 10 | update-types: ["version-update:semver-patch"] 11 | 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /.github/workflows/license.yml: -------------------------------------------------------------------------------- 1 | name: license 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 1 1 *" 6 | 7 | concurrency: 8 | group: ${{ github.workflow }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | license: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | 20 | - uses: FantasticFiasco/action-update-license-year@v3 21 | with: 22 | token: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js modules 2 | **/node_modules/ 3 | 4 | # Builded sources 5 | **/build/ 6 | **/dist/ 7 | 8 | # Coverage 9 | **/coverage/ 10 | 11 | # IntelliJ stuffs 12 | .idea/ 13 | 14 | # Parcel cache 15 | .cache 16 | .parcel-cache 17 | 18 | # Sponsorkit cache 19 | ./images/.cache.json 20 | 21 | # Environments 22 | .env 23 | 24 | # Archives 25 | *.tar.gz 26 | *.tgz 27 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # shellcheck disable=SC1007 source=SCRIPTDIR/_/husky.sh 4 | . "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/_/husky.sh" 5 | 6 | npx --no -- lint-staged 7 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "**/*.ts": ["eslint --fix", "prettier --write"], 3 | "**/*.md": ["markdownlint --fix", "prettier --write"], 4 | "!**/*.{ts,md}": "prettier --write --ignore-unknown" 5 | } 6 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "line-length": false, 4 | "no-blanks-blockquote": false 5 | } 6 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | **/build/ 2 | **/dist/ 3 | **/coverage/ 4 | **/node_modules/ 5 | website/blog/2018-03-25-medium-article.md 6 | website/blog/2020-08-19-devto-article.md 7 | website/versioned_docs/version-0.16.0/ 8 | website/versioned_docs/version-0.17.0/ 9 | website/versioned_docs/version-0.17.1/ 10 | website/versioned_docs/version-0.17.2/ 11 | website/versioned_docs/version-0.17.4/ 12 | website/versioned_docs/version-0.17.5/ 13 | website/versioned_docs/version-0.17.6/ 14 | website/versioned_docs/version-1.0.0/ 15 | website/versioned_docs/version-1.1.0/ 16 | website/versioned_docs/version-1.1.1/ 17 | website/versioned_docs/version-1.2.0-rc.1/ 18 | 19 | # FIXME: Remove 20 | CHANGELOG.md 21 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/build/ 2 | **/dist/ 3 | **/coverage/ 4 | **/node_modules/ 5 | /.husky/_/ 6 | /.gitattributes 7 | website/versioned_docs 8 | website/versioned_sidebars 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "trailingComma": "all", 4 | "tabWidth": 2, 5 | "printWidth": 100, 6 | "bracketSpacing": true, 7 | "semi": true, 8 | "singleQuote": false, 9 | "arrowParens": "avoid", 10 | "useTabs": false 11 | } 12 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | external-sources=true 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bierner.comment-tagged-templates", 4 | "DavidAnson.vscode-markdownlint", 5 | "dbaeumer.vscode-eslint", 6 | "EditorConfig.EditorConfig", 7 | "esbenp.prettier-vscode", 8 | "GitHub.vscode-github-actions", 9 | "GraphQL.vscode-graphql", 10 | "streetsidesoftware.code-spell-checker", 11 | "timonwong.shellcheck", 12 | "tlent.jest-snapshot-language-support", 13 | "wayou.vscode-todo-highlight" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "${workspaceFolder}/node_modules/typescript/lib", 3 | "editor.formatOnSave": true, 4 | "editor.defaultFormatter": "esbenp.prettier-vscode", 5 | "[jsonc]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll.eslint": "explicit" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /benchmarks/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "max-classes-per-file": "off", 5 | "class-methods-use-this": "off", 6 | "import/no-extraneous-dependencies": [ 7 | "error", 8 | { 9 | "devDependencies": true 10 | } 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /benchmarks/array/results.txt: -------------------------------------------------------------------------------- 1 | Core i7 2700K @ 3.5GHz 2 | Windows 10 x64 3 | 25 000 array items | 50 iterations 4 | Node.js v13.5 5 | 6 | ----- 7 | TypeGraphQL 8 | 9 | standard 10 | - 15.518s 11 | 12 | using sync field resolvers 13 | - 18.180s 14 | 15 | using async field resolvers 16 | - 39.934s 17 | 18 | using getters 19 | - 31.207s 20 | 21 | standard with global middleware 22 | - 62.664s 23 | 24 | with `simpleResolvers: true` 25 | - 14.980s 26 | 27 | ----- 28 | `graphql-js` 29 | 30 | standard 31 | - 13.276s 32 | 33 | async field resolvers 34 | - 25.630s 35 | -------------------------------------------------------------------------------- /benchmarks/simple/results.txt: -------------------------------------------------------------------------------- 1 | Core i7 2700K @ 3.5GHz 2 | Windows 10 x64 3 | 100 000 iterations 4 | 5 | ----- 6 | Node.js v10.17 7 | 8 | ES2016 build 9 | - singleObject: 4831.113ms 10 | - nestedObject: 17067.581ms 11 | 12 | ES2018 build 13 | - singleObject: 2196.958ms 14 | - nestedObject: 7311.419ms 15 | 16 | graphql-js 17 | - singleObject: 871.424ms 18 | - nestedObject: 2206.372ms 19 | 20 | ----- 21 | Node.js v13.2 22 | 23 | ES2018 build 24 | - singleObject: 1.622s 25 | - nestedObject: 4.557s 26 | 27 | without field resolvers (`graphql-js` implicit ones) 28 | - singleObject: 1.465s 29 | - nestedObject: 3.086s 30 | 31 | graphql-js 32 | - singleObject: 1.003s 33 | - nestedObject: 2.422s 34 | -------------------------------------------------------------------------------- /benchmarks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build" 5 | }, 6 | "include": [".", "../src"] 7 | } 8 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | **The documentation has been prepared to be viewed on [TypeGraphQL website](https://typegraphql.com), please read it there.** 4 | -------------------------------------------------------------------------------- /examples/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": "off", 4 | "class-methods-use-this": "off", 5 | "import/no-cycle": "off", 6 | "import/no-extraneous-dependencies": ["error", { "devDependencies": true }] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/apollo-cache/cache-control.ts: -------------------------------------------------------------------------------- 1 | import { type CacheHint } from "@apollo/cache-control-types"; 2 | import { Directive } from "type-graphql"; 3 | import { type RequireAtLeastOne } from "./helpers/RequireAtLeastOne"; 4 | 5 | export function CacheControl({ maxAge, scope }: RequireAtLeastOne) { 6 | if (maxAge === undefined && scope === undefined) { 7 | throw new Error("Missing maxAge or scope param for @CacheControl"); 8 | } 9 | 10 | let sdl = "@cacheControl("; 11 | if (maxAge !== undefined) { 12 | sdl += `maxAge: ${maxAge}`; 13 | } 14 | if (scope) { 15 | sdl += ` scope: ${scope}`; 16 | } 17 | sdl += ")"; 18 | 19 | return Directive(sdl); 20 | } 21 | -------------------------------------------------------------------------------- /examples/apollo-cache/examples.graphql: -------------------------------------------------------------------------------- 1 | query CachedQuery { 2 | cachedRecipe(title: "Recipe 1") { 3 | title 4 | description 5 | cachedAverageRating 6 | } 7 | } 8 | 9 | query NotCachedQuery { 10 | recipe(title: "Recipe 1") { 11 | title 12 | description 13 | averageRating 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/apollo-cache/helpers/RequireAtLeastOne.d.ts: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/a/49725198/6676781 2 | 3 | export type RequireAtLeastOne = Pick> & 4 | { 5 | [K in Keys]-?: Required> & Partial>>; 6 | }[Keys]; 7 | -------------------------------------------------------------------------------- /examples/apollo-cache/helpers/getTime.ts: -------------------------------------------------------------------------------- 1 | export function getTime(date: Date = new Date()) { 2 | return date.toTimeString().slice(0, 8); 3 | } 4 | -------------------------------------------------------------------------------- /examples/apollo-cache/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial) { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export function createRecipeSamples() { 8 | return [ 9 | createRecipe({ 10 | title: "Recipe 1", 11 | description: "Desc 1", 12 | ratings: [0, 3, 1], 13 | creationDate: new Date("2018-04-11"), 14 | }), 15 | createRecipe({ 16 | title: "Recipe 2", 17 | description: "Desc 2", 18 | ratings: [4, 2, 3, 1], 19 | creationDate: new Date("2018-04-15"), 20 | }), 21 | createRecipe({ 22 | title: "Recipe 3", 23 | description: "Desc 3", 24 | ratings: [5, 4], 25 | creationDate: new Date(), 26 | }), 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /examples/apollo-cache/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | """ 7 | A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.This scalar is serialized to a string in ISO 8601 format and parsed from a string in ISO 8601 format. 8 | """ 9 | scalar DateTimeISO 10 | 11 | type Query { 12 | cachedRecipe(title: String!): Recipe 13 | recipe(title: String!): Recipe 14 | recipes: [Recipe!]! 15 | } 16 | 17 | type Recipe { 18 | averageRating: Float 19 | cachedAverageRating: Float 20 | creationDate: DateTimeISO! 21 | description: String 22 | ratings: [Int!]! 23 | title: String! 24 | } 25 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/accounts/data.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./user"; 2 | 3 | function createUser(userData: Partial) { 4 | return Object.assign(new User(), userData); 5 | } 6 | 7 | export const users: User[] = [ 8 | createUser({ 9 | id: "1", 10 | name: "Ada Lovelace", 11 | birthDate: "1815-12-10", 12 | username: "@ada", 13 | }), 14 | createUser({ 15 | id: "2", 16 | name: "Alan Turing", 17 | birthDate: "1912-06-23", 18 | username: "@complete", 19 | }), 20 | ]; 21 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/accounts/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from "@apollo/server"; 2 | import { startStandaloneServer } from "@apollo/server/standalone"; 3 | import { AccountsResolver } from "./resolver"; 4 | import { User } from "./user"; 5 | import { resolveUserReference } from "./user.reference"; 6 | import { buildFederatedSchema } from "../helpers/buildFederatedSchema"; 7 | 8 | export async function listen(port: number): Promise { 9 | const schema = await buildFederatedSchema( 10 | { 11 | resolvers: [AccountsResolver], 12 | orphanedTypes: [User], 13 | }, 14 | { 15 | User: { __resolveReference: resolveUserReference }, 16 | }, 17 | ); 18 | 19 | const server = new ApolloServer({ schema }); 20 | 21 | const { url } = await startStandaloneServer(server, { listen: { port } }); 22 | console.log(`Accounts service ready at ${url}`); 23 | 24 | return url; 25 | } 26 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/accounts/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Query, Resolver } from "type-graphql"; 2 | import { users } from "./data"; 3 | import { User } from "./user"; 4 | 5 | @Resolver(_of => User) 6 | export class AccountsResolver { 7 | @Query(_returns => User) 8 | me(): User { 9 | return users[0]; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/accounts/user.reference.ts: -------------------------------------------------------------------------------- 1 | import { users } from "./data"; 2 | import { type User } from "./user"; 3 | 4 | export async function resolveUserReference(reference: Pick): Promise { 5 | return users.find(u => u.id === reference.id)!; 6 | } 7 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/accounts/user.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ID, ObjectType } from "type-graphql"; 2 | 3 | @Directive(`@key(fields: "id")`) 4 | @ObjectType() 5 | export class User { 6 | @Field(_type => ID) 7 | id!: string; 8 | 9 | @Directive("@shareable") 10 | @Field() 11 | username!: string; 12 | 13 | @Field() 14 | name!: string; 15 | 16 | @Field() 17 | birthDate!: string; 18 | } 19 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/examples.graphql: -------------------------------------------------------------------------------- 1 | query { 2 | topProducts { 3 | __typename 4 | name 5 | price 6 | shippingEstimate 7 | inStock 8 | reviews { 9 | body 10 | author { 11 | name 12 | birthDate 13 | reviews { 14 | product { 15 | __typename 16 | name 17 | } 18 | body 19 | } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/inventory/data.ts: -------------------------------------------------------------------------------- 1 | export interface Inventory { 2 | upc: string; 3 | inStock: boolean; 4 | } 5 | 6 | export const inventory: Inventory[] = [ 7 | { upc: "1", inStock: true }, 8 | { upc: "2", inStock: false }, 9 | { upc: "3", inStock: true }, 10 | ]; 11 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/inventory/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from "@apollo/server"; 2 | import { startStandaloneServer } from "@apollo/server/standalone"; 3 | import { Product } from "./product"; 4 | import { resolveProductReference } from "./product.reference"; 5 | import { InventoryResolver } from "./resolver"; 6 | import { buildFederatedSchema } from "../helpers/buildFederatedSchema"; 7 | 8 | export async function listen(port: number): Promise { 9 | const schema = await buildFederatedSchema( 10 | { 11 | resolvers: [InventoryResolver], 12 | orphanedTypes: [Product], 13 | }, 14 | { 15 | Product: { __resolveReference: resolveProductReference }, 16 | }, 17 | ); 18 | 19 | const server = new ApolloServer({ schema }); 20 | 21 | const { url } = await startStandaloneServer(server, { listen: { port } }); 22 | console.log(`Inventory service ready at ${url}`); 23 | 24 | return url; 25 | } 26 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/inventory/product.reference.ts: -------------------------------------------------------------------------------- 1 | import { inventory } from "./data"; 2 | import { Product } from "./product"; 3 | 4 | export async function resolveProductReference( 5 | reference: Pick, 6 | ): Promise { 7 | const found = inventory.find(i => i.upc === reference.upc); 8 | 9 | if (!found) { 10 | return undefined; 11 | } 12 | 13 | return Object.assign(new Product(), { 14 | ...reference, 15 | ...found, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/inventory/product.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | @Directive("@extends") 5 | @Directive("@interfaceObject") 6 | @Directive(`@key(fields: "upc")`) 7 | export class Product { 8 | @Field() 9 | @Directive("@external") 10 | upc!: string; 11 | 12 | @Field() 13 | @Directive("@external") 14 | weight!: number; 15 | 16 | @Field() 17 | @Directive("@external") 18 | price!: number; 19 | 20 | @Field() 21 | inStock!: boolean; 22 | } 23 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/inventory/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Directive, FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Product } from "./product"; 3 | 4 | @Resolver(_of => Product) 5 | export class InventoryResolver { 6 | @Directive(`@requires(fields: "price weight")`) 7 | @FieldResolver(_returns => Number) 8 | async shippingEstimate(@Root() product: Product): Promise { 9 | // free for expensive items 10 | if (product.price > 1000) { 11 | return 0; 12 | } 13 | 14 | // estimate is based on weight 15 | return product.weight * 0.5; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/data.ts: -------------------------------------------------------------------------------- 1 | import { Dining } from "./dining"; 2 | import { type Product } from "./product"; 3 | import { Seating } from "./seating"; 4 | 5 | export const products: Product[] = [ 6 | Object.assign(new Dining(), { 7 | upc: "1", 8 | name: "Table", 9 | price: 899, 10 | weight: 100, 11 | height: "3ft", 12 | }), 13 | Object.assign(new Seating(), { 14 | upc: "2", 15 | name: "Couch", 16 | price: 1299, 17 | weight: 1000, 18 | seats: 2, 19 | }), 20 | Object.assign(new Seating(), { 21 | upc: "3", 22 | name: "Chair", 23 | price: 54, 24 | weight: 50, 25 | seats: 1, 26 | }), 27 | ]; 28 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/dining.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | import { Product } from "./product"; 3 | 4 | @Directive(`@key(fields: "upc")`) 5 | @ObjectType({ implements: Product }) 6 | export class Dining extends Product { 7 | @Field() 8 | height!: string; 9 | } 10 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from "@apollo/server"; 2 | import { startStandaloneServer } from "@apollo/server/standalone"; 3 | import { Product } from "./product"; 4 | import { resolveProductReference } from "./product.reference"; 5 | import { ProductsResolver } from "./resolver"; 6 | import { buildFederatedSchema } from "../helpers/buildFederatedSchema"; 7 | 8 | export async function listen(port: number): Promise { 9 | const schema = await buildFederatedSchema( 10 | { 11 | resolvers: [ProductsResolver], 12 | orphanedTypes: [Product], 13 | }, 14 | { 15 | Product: { __resolveReference: resolveProductReference }, 16 | }, 17 | ); 18 | 19 | const server = new ApolloServer({ schema }); 20 | 21 | const { url } = await startStandaloneServer(server, { listen: { port } }); 22 | console.log(`Products service ready at ${url}`); 23 | 24 | return url; 25 | } 26 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/product.reference.ts: -------------------------------------------------------------------------------- 1 | import { products } from "./data"; 2 | import { type Product } from "./product"; 3 | 4 | export async function resolveProductReference( 5 | reference: Pick, 6 | ): Promise { 7 | return products.find(p => p.upc === reference.upc); 8 | } 9 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/product.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, InterfaceType } from "type-graphql"; 2 | 3 | @Directive(`@key(fields: "upc")`) 4 | @InterfaceType() 5 | export abstract class Product { 6 | @Field() 7 | upc!: string; 8 | 9 | @Field() 10 | name!: string; 11 | 12 | @Field() 13 | price!: number; 14 | 15 | @Field() 16 | weight!: number; 17 | } 18 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Arg, Query, Resolver } from "type-graphql"; 2 | import { products } from "./data"; 3 | import { Product } from "./product"; 4 | 5 | @Resolver(_of => Product) 6 | export class ProductsResolver { 7 | @Query(_returns => [Product]) 8 | async topProducts( 9 | @Arg("first", { defaultValue: 5 }) 10 | first: number, 11 | ): Promise { 12 | return products.slice(0, first); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/products/seating.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | import { Product } from "./product"; 3 | 4 | @Directive(`@key(fields: "upc")`) 5 | @ObjectType({ implements: Product }) 6 | export class Seating extends Product { 7 | @Field() 8 | seats!: number; 9 | } 10 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/reviews/product/product.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | 3 | @Directive("@extends") 4 | @Directive(`@key(fields: "upc")`) 5 | @Directive("@interfaceObject") 6 | @ObjectType() 7 | export class Product { 8 | @Directive("@external") 9 | @Field() 10 | upc!: string; 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/reviews/product/resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Product } from "./product"; 3 | import { reviews } from "../review/data"; 4 | import { Review } from "../review/review"; 5 | 6 | @Resolver(_of => Product) 7 | export class ProductReviewsResolver { 8 | @FieldResolver(() => [Review]) 9 | async reviews(@Root() product: Product): Promise { 10 | return reviews.filter(review => review.product.upc === product.upc); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/reviews/review/resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver } from "type-graphql"; 2 | import { reviews } from "./data"; 3 | import { Review } from "./review"; 4 | 5 | @Resolver(_of => Review) 6 | export class ReviewsResolver { 7 | @FieldResolver(_returns => [Review]) 8 | async reviews(): Promise { 9 | return reviews; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/reviews/review/review.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ID, ObjectType } from "type-graphql"; 2 | import { Product } from "../product/product"; 3 | import { User } from "../user/user"; 4 | 5 | @Directive(`@key(fields: "id")`) 6 | @ObjectType() 7 | export class Review { 8 | @Field(_type => ID) 9 | id!: string; 10 | 11 | @Field() 12 | body!: string; 13 | 14 | @Directive(`@provides(fields: "username")`) 15 | @Field() 16 | author!: User; 17 | 18 | @Field() 19 | product!: Product; 20 | } 21 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/reviews/user/resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { User } from "./user"; 3 | import { reviews } from "../review/data"; 4 | import { Review } from "../review/review"; 5 | 6 | @Resolver(_of => User) 7 | export class UserReviewsResolver { 8 | @FieldResolver(_returns => [Review]) 9 | async reviews(@Root() user: User): Promise { 10 | return reviews.filter(review => review.author.id === user.id); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/apollo-federation-2/reviews/user/user.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ID, ObjectType } from "type-graphql"; 2 | 3 | @Directive(`@key(fields: "id")`) 4 | @ObjectType() 5 | export class User { 6 | @Field(_type => ID) 7 | id!: string; 8 | 9 | @Directive("@external") 10 | @Field() 11 | username!: string; 12 | } 13 | -------------------------------------------------------------------------------- /examples/apollo-federation/accounts/data.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./user"; 2 | 3 | function createUser(userData: Partial) { 4 | return Object.assign(new User(), userData); 5 | } 6 | 7 | export const users: User[] = [ 8 | createUser({ 9 | id: "1", 10 | name: "Ada Lovelace", 11 | birthDate: "1815-12-10", 12 | username: "@ada", 13 | }), 14 | createUser({ 15 | id: "2", 16 | name: "Alan Turing", 17 | birthDate: "1912-06-23", 18 | username: "@complete", 19 | }), 20 | ]; 21 | -------------------------------------------------------------------------------- /examples/apollo-federation/accounts/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Query, Resolver } from "type-graphql"; 2 | import { users } from "./data"; 3 | import { User } from "./user"; 4 | 5 | @Resolver(_of => User) 6 | export class AccountsResolver { 7 | @Query(_returns => User) 8 | me(): User { 9 | return users[0]; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation/accounts/user.reference.ts: -------------------------------------------------------------------------------- 1 | import { users } from "./data"; 2 | import { type User } from "./user"; 3 | 4 | export async function resolveUserReference(reference: Pick): Promise { 5 | return users.find(u => u.id === reference.id)!; 6 | } 7 | -------------------------------------------------------------------------------- /examples/apollo-federation/accounts/user.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ID, ObjectType } from "type-graphql"; 2 | 3 | @Directive(`@key(fields: "id")`) 4 | @ObjectType() 5 | export class User { 6 | @Field(_type => ID) 7 | id!: string; 8 | 9 | @Field() 10 | username!: string; 11 | 12 | @Field() 13 | name!: string; 14 | 15 | @Field() 16 | birthDate!: string; 17 | } 18 | -------------------------------------------------------------------------------- /examples/apollo-federation/examples.graphql: -------------------------------------------------------------------------------- 1 | query { 2 | topProducts { 3 | name 4 | price 5 | shippingEstimate 6 | inStock 7 | reviews { 8 | body 9 | author { 10 | name 11 | birthDate 12 | reviews { 13 | product { 14 | name 15 | } 16 | body 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/apollo-federation/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./buildFederatedSchema"; 2 | -------------------------------------------------------------------------------- /examples/apollo-federation/inventory/data.ts: -------------------------------------------------------------------------------- 1 | export interface Inventory { 2 | upc: string; 3 | inStock: boolean; 4 | } 5 | 6 | export const inventory: Inventory[] = [ 7 | { upc: "1", inStock: true }, 8 | { upc: "2", inStock: false }, 9 | { upc: "3", inStock: true }, 10 | ]; 11 | -------------------------------------------------------------------------------- /examples/apollo-federation/inventory/product.reference.ts: -------------------------------------------------------------------------------- 1 | import { inventory } from "./data"; 2 | import { Product } from "./product"; 3 | 4 | export async function resolveProductReference( 5 | reference: Pick, 6 | ): Promise { 7 | const found = inventory.find(i => i.upc === reference.upc); 8 | 9 | if (!found) { 10 | return; 11 | } 12 | 13 | // eslint-disable-next-line consistent-return 14 | return Object.assign(new Product(), { 15 | ...reference, 16 | ...found, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /examples/apollo-federation/inventory/product.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | @Directive("@extends") 5 | @Directive(`@key(fields: "upc")`) 6 | export class Product { 7 | @Field() 8 | @Directive("@external") 9 | upc!: string; 10 | 11 | @Field() 12 | @Directive("@external") 13 | weight!: number; 14 | 15 | @Field() 16 | @Directive("@external") 17 | price!: number; 18 | 19 | @Field() 20 | inStock!: boolean; 21 | } 22 | -------------------------------------------------------------------------------- /examples/apollo-federation/inventory/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Directive, FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Product } from "./product"; 3 | 4 | @Resolver(_of => Product) 5 | export class InventoryResolver { 6 | @Directive(`@requires(fields: "price weight")`) 7 | @FieldResolver(_returns => Number) 8 | async shippingEstimate(@Root() product: Product): Promise { 9 | // Free for expensive items 10 | if (product.price > 1000) { 11 | return 0; 12 | } 13 | 14 | // Estimate is based on weight 15 | return product.weight * 0.5; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/apollo-federation/products/data.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product"; 2 | 3 | function createProduct(productData: Partial) { 4 | return Object.assign(new Product(), productData); 5 | } 6 | 7 | export const products: Product[] = [ 8 | createProduct({ 9 | upc: "1", 10 | name: "Table", 11 | price: 899, 12 | weight: 100, 13 | }), 14 | createProduct({ 15 | upc: "2", 16 | name: "Couch", 17 | price: 1299, 18 | weight: 1000, 19 | }), 20 | createProduct({ 21 | upc: "3", 22 | name: "Chair", 23 | price: 54, 24 | weight: 50, 25 | }), 26 | ]; 27 | -------------------------------------------------------------------------------- /examples/apollo-federation/products/product.reference.ts: -------------------------------------------------------------------------------- 1 | import { products } from "./data"; 2 | import { type Product } from "./product"; 3 | 4 | export async function resolveProductReference( 5 | reference: Pick, 6 | ): Promise { 7 | return products.find(p => p.upc === reference.upc); 8 | } 9 | -------------------------------------------------------------------------------- /examples/apollo-federation/products/product.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | 3 | @Directive(`@key(fields: "upc")`) 4 | @ObjectType() 5 | export class Product { 6 | @Field() 7 | upc!: string; 8 | 9 | @Field() 10 | name!: string; 11 | 12 | @Field() 13 | price!: number; 14 | 15 | @Field() 16 | weight!: number; 17 | } 18 | -------------------------------------------------------------------------------- /examples/apollo-federation/products/resolver.ts: -------------------------------------------------------------------------------- 1 | import { Arg, Query, Resolver } from "type-graphql"; 2 | import { products } from "./data"; 3 | import { Product } from "./product"; 4 | 5 | @Resolver(_of => Product) 6 | export class ProductsResolver { 7 | @Query(_returns => [Product]) 8 | async topProducts( 9 | @Arg("first", { defaultValue: 5 }) 10 | first: number, 11 | ): Promise { 12 | return products.slice(0, first); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/index.ts: -------------------------------------------------------------------------------- 1 | import { ApolloServer } from "@apollo/server"; 2 | import { startStandaloneServer } from "@apollo/server/standalone"; 3 | import { Product, ProductReviewsResolver } from "./product"; 4 | import { Review, ReviewsResolver } from "./review"; 5 | import { User, UserReviewsResolver } from "./user"; 6 | import { buildFederatedSchema } from "../helpers"; 7 | 8 | export async function listen(port: number): Promise { 9 | // Build TypeGraphQL executable schema 10 | const schema = await buildFederatedSchema({ 11 | resolvers: [ReviewsResolver, ProductReviewsResolver, UserReviewsResolver], 12 | orphanedTypes: [User, Review, Product], 13 | }); 14 | 15 | // Create GraphQL server 16 | const server = new ApolloServer({ schema }); 17 | 18 | // Start server 19 | const { url } = await startStandaloneServer(server, { listen: { port } }); 20 | console.log(`Reviews service ready at ${url}`); 21 | 22 | return url; 23 | } 24 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/product/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./product"; 2 | 3 | export * from "./resolver"; 4 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/product/product.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ObjectType } from "type-graphql"; 2 | 3 | @Directive("@extends") 4 | @Directive(`@key(fields: "upc")`) 5 | @ObjectType() 6 | export class Product { 7 | @Directive("@external") 8 | @Field() 9 | upc!: string; 10 | } 11 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/product/resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Product } from "./product"; 3 | import { Review, reviews } from "../review"; 4 | 5 | @Resolver(_of => Product) 6 | export class ProductReviewsResolver { 7 | @FieldResolver(() => [Review]) 8 | async reviews(@Root() product: Product): Promise { 9 | return reviews.filter(review => review.product.upc === product.upc); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/review/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./data"; 2 | export * from "./resolver"; 3 | export * from "./review"; 4 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/review/resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver } from "type-graphql"; 2 | import { reviews } from "./data"; 3 | import { Review } from "./review"; 4 | 5 | @Resolver(_of => Review) 6 | export class ReviewsResolver { 7 | @FieldResolver(_returns => [Review]) 8 | async reviews(): Promise { 9 | return reviews; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/review/review.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ID, ObjectType } from "type-graphql"; 2 | import { Product } from "../product"; 3 | import { User } from "../user"; 4 | 5 | @Directive(`@key(fields: "id")`) 6 | @ObjectType() 7 | export class Review { 8 | @Field(_type => ID) 9 | id!: string; 10 | 11 | @Field() 12 | body!: string; 13 | 14 | @Directive(`@provides(fields: "username")`) 15 | @Field() 16 | author!: User; 17 | 18 | @Field() 19 | product!: Product; 20 | } 21 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resolver"; 2 | export * from "./user"; 3 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/user/resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { User } from "./user"; 3 | import { Review, reviews } from "../review"; 4 | 5 | @Resolver(_of => User) 6 | export class UserReviewsResolver { 7 | @FieldResolver(_returns => [Review]) 8 | async reviews(@Root() user: User): Promise { 9 | return reviews.filter(review => review.author.id === user.id); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/apollo-federation/reviews/user/user.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Field, ID, ObjectType } from "type-graphql"; 2 | 3 | @Directive("@extends") 4 | @Directive(`@key(fields: "id")`) 5 | @ObjectType() 6 | export class User { 7 | @Directive("@external") 8 | @Field(_type => ID) 9 | id!: string; 10 | 11 | @Directive("@external") 12 | @Field() 13 | username!: string; 14 | } 15 | -------------------------------------------------------------------------------- /examples/apollo-federation/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Product { 7 | inStock: Boolean! 8 | name: String! 9 | price: Float! 10 | reviews: [Review!]! 11 | shippingEstimate: Float! 12 | upc: String! 13 | weight: Float! 14 | } 15 | 16 | type Query { 17 | me: User! 18 | topProducts(first: Float! = 5): [Product!]! 19 | } 20 | 21 | type Review { 22 | author: User! 23 | body: String! 24 | id: ID! 25 | product: Product! 26 | reviews: [Review!]! 27 | } 28 | 29 | type User { 30 | birthDate: String! 31 | id: ID! 32 | name: String! 33 | reviews: [Review!]! 34 | username: String! 35 | } 36 | -------------------------------------------------------------------------------- /examples/authorization/auth-checker.ts: -------------------------------------------------------------------------------- 1 | import { type AuthChecker } from "type-graphql"; 2 | import { type Context } from "./context.type"; 3 | 4 | // Auth checker function 5 | export const authChecker: AuthChecker = ({ context: { user } }, roles) => { 6 | // Check user 7 | if (!user) { 8 | // No user, restrict access 9 | return false; 10 | } 11 | 12 | // Check '@Authorized()' 13 | if (roles.length === 0) { 14 | // Only authentication required 15 | return true; 16 | } 17 | 18 | // Check '@Authorized(...)' roles overlap 19 | return user.roles.some(role => roles.includes(role)); 20 | }; 21 | -------------------------------------------------------------------------------- /examples/authorization/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type User } from "./user.type"; 2 | 3 | export interface Context { 4 | user?: User; 5 | } 6 | -------------------------------------------------------------------------------- /examples/authorization/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetPublicRecipes { 2 | recipes { 3 | title 4 | description 5 | averageRating 6 | } 7 | } 8 | 9 | query GetRecipesForAuthorizedUser { 10 | recipes { 11 | title 12 | description 13 | ingredients 14 | averageRating 15 | } 16 | } 17 | 18 | query GetRecipesForAdmin { 19 | recipes { 20 | title 21 | description 22 | ingredients 23 | averageRating 24 | ratings 25 | } 26 | } 27 | 28 | mutation AddRecipeByAuthorizedUser { 29 | addRecipe(title: "Sample Recipe") { 30 | averageRating 31 | } 32 | } 33 | 34 | mutation DeleteRecipeByAdmin { 35 | deleteRecipe(title: "Recipe 1") 36 | } 37 | -------------------------------------------------------------------------------- /examples/authorization/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial): Recipe { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export const sampleRecipes = [ 8 | createRecipe({ 9 | title: "Recipe 1", 10 | description: "Desc 1", 11 | ingredients: ["one", "two", "three"], 12 | ratings: [3, 4, 5, 5, 5], 13 | }), 14 | createRecipe({ 15 | title: "Recipe 2", 16 | description: "Desc 2", 17 | ingredients: ["four", "five", "six"], 18 | ratings: [3, 4, 5, 3, 2], 19 | }), 20 | createRecipe({ 21 | title: "Recipe 3", 22 | ingredients: ["seven", "eight", "nine"], 23 | ratings: [4, 4, 5, 5, 4], 24 | }), 25 | ]; 26 | -------------------------------------------------------------------------------- /examples/authorization/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Authorized, Field, Float, Int, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field() 6 | title!: string; 7 | 8 | @Field({ nullable: true }) 9 | description?: string; 10 | 11 | @Authorized() // Restrict access only for authenticated users 12 | @Field(_type => [String]) 13 | ingredients!: string[]; 14 | 15 | @Authorized("ADMIN") // Restrict access only for 'ADMIN' users 16 | @Field(_type => [Int]) 17 | ratings!: number[]; 18 | 19 | @Field(_type => Float, { nullable: true }) 20 | get averageRating(): number | null { 21 | if (!this.ratings.length) { 22 | return null; 23 | } 24 | return this.ratings.reduce((a, b) => a + b, 0) / this.ratings.length; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/authorization/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | addRecipe(description: String, title: String!): Recipe! 8 | deleteRecipe(title: String!): Boolean! 9 | } 10 | 11 | type Query { 12 | recipes: [Recipe!]! 13 | } 14 | 15 | type Recipe { 16 | averageRating: Float 17 | description: String 18 | ingredients: [String!]! 19 | ratings: [Int!]! 20 | title: String! 21 | } 22 | -------------------------------------------------------------------------------- /examples/authorization/user.type.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | name: string; 4 | roles: string[]; 5 | } 6 | -------------------------------------------------------------------------------- /examples/automatic-validation/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes { 3 | title 4 | description 5 | creationDate 6 | } 7 | } 8 | 9 | mutation CorrectAddRecipe { 10 | addRecipe( 11 | input: { 12 | title: "Correct title" 13 | description: "Very very very very very very very very long description" 14 | } 15 | ) { 16 | creationDate 17 | } 18 | } 19 | 20 | mutation AddRecipeWithoutDesc { 21 | addRecipe(input: { title: "Correct title" }) { 22 | creationDate 23 | } 24 | } 25 | 26 | mutation IncorrectAddRecipe { 27 | addRecipe(input: { title: "Correct title", description: "Too short description" }) { 28 | creationDate 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/automatic-validation/helpers.ts: -------------------------------------------------------------------------------- 1 | import { type Recipe } from "./recipe.type"; 2 | 3 | export function generateRecipes(count: number): Recipe[] { 4 | return new Array(count).fill(null).map( 5 | (_, i): Recipe => ({ 6 | title: `Recipe #${i + 1}`, 7 | description: `Description #${i + 1}`, 8 | creationDate: new Date(), 9 | }), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/automatic-validation/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Length, MaxLength } from "class-validator"; 2 | import { Field, InputType } from "type-graphql"; 3 | import { type Recipe } from "./recipe.type"; 4 | 5 | @InputType() 6 | export class RecipeInput implements Partial { 7 | @Field() 8 | @MaxLength(30) 9 | title!: string; 10 | 11 | @Field({ nullable: true }) 12 | @Length(30, 255) 13 | description?: string; 14 | } 15 | -------------------------------------------------------------------------------- /examples/automatic-validation/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field() 6 | title!: string; 7 | 8 | @Field({ nullable: true }) 9 | description?: string; 10 | 11 | @Field() 12 | creationDate!: Date; 13 | } 14 | -------------------------------------------------------------------------------- /examples/automatic-validation/recipes.arguments.ts: -------------------------------------------------------------------------------- 1 | import { Max, Min } from "class-validator"; 2 | import { ArgsType, Field, Int } from "type-graphql"; 3 | 4 | @ArgsType() 5 | export class RecipesArguments { 6 | @Field(_type => Int) 7 | @Min(0) 8 | skip = 0; 9 | 10 | @Field(_type => Int) 11 | @Min(1) 12 | @Max(50) 13 | take = 10; 14 | } 15 | -------------------------------------------------------------------------------- /examples/custom-validation/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes { 3 | title 4 | description 5 | creationDate 6 | } 7 | } 8 | 9 | mutation CorrectAddRecipe { 10 | addRecipe( 11 | input: { 12 | title: "Correct title" 13 | description: "Very very very very very very very very long description" 14 | } 15 | ) { 16 | creationDate 17 | } 18 | } 19 | 20 | mutation AddRecipeWithoutDesc { 21 | addRecipe(input: { title: "Correct title" }) { 22 | creationDate 23 | } 24 | } 25 | 26 | mutation IncorrectAddRecipe { 27 | addRecipe(input: { title: "Correct title", description: "Too short description" }) { 28 | creationDate 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/custom-validation/helpers.ts: -------------------------------------------------------------------------------- 1 | import { type Recipe } from "./recipe.type"; 2 | 3 | export function generateRecipes(count: number): Recipe[] { 4 | return new Array(count).fill(null).map( 5 | (_, i): Recipe => ({ 6 | title: `Recipe #${i + 1}`, 7 | description: `Description #${i + 1}`, 8 | creationDate: new Date(), 9 | }), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/custom-validation/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import Joiful from "joiful"; 2 | import { Field, InputType } from "type-graphql"; 3 | import { type Recipe } from "./recipe.type"; 4 | 5 | @InputType() 6 | export class RecipeInput implements Partial { 7 | @Field() 8 | // Joi decorator 9 | @(Joiful.string().required().max(30)) 10 | title!: string; 11 | 12 | @Field({ nullable: true }) 13 | // Joi decorator 14 | @(Joiful.string().min(30).max(255)) 15 | description?: string; 16 | } 17 | -------------------------------------------------------------------------------- /examples/custom-validation/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field() 6 | title!: string; 7 | 8 | @Field({ nullable: true }) 9 | description?: string; 10 | 11 | @Field() 12 | creationDate!: Date; 13 | } 14 | -------------------------------------------------------------------------------- /examples/custom-validation/recipes.arguments.ts: -------------------------------------------------------------------------------- 1 | import Joiful from "joiful"; 2 | import { ArgsType, Field, Int } from "type-graphql"; 3 | 4 | @ArgsType() 5 | export class RecipesArguments { 6 | @Field(_type => Int) 7 | // use decorators for Joi 8 | @(Joiful.number().min(0)) 9 | skip = 0; 10 | 11 | @Field(_type => Int) 12 | // use decorators for Joi 13 | @(Joiful.number().min(1).max(50)) 14 | take = 10; 15 | } 16 | -------------------------------------------------------------------------------- /examples/custom-validation/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | """ 7 | The javascript `Date` as string. Type represents date and time as the ISO Date string. 8 | """ 9 | scalar DateTime 10 | 11 | type Mutation { 12 | addRecipe(input: RecipeInput!): Recipe! 13 | } 14 | 15 | type Query { 16 | recipes(skip: Int! = 0, take: Int! = 10): [Recipe!]! 17 | } 18 | 19 | type Recipe { 20 | creationDate: DateTime! 21 | description: String 22 | title: String! 23 | } 24 | 25 | input RecipeInput { 26 | description: String 27 | title: String! 28 | } 29 | -------------------------------------------------------------------------------- /examples/enums-and-unions/cook.data.ts: -------------------------------------------------------------------------------- 1 | import { Cook } from "./cook.type"; 2 | 3 | function createCook(cookData: Partial): Cook { 4 | return Object.assign(new Cook(), cookData); 5 | } 6 | 7 | export const sampleCooks = [ 8 | createCook({ 9 | name: "Gordon Ramsay", 10 | yearsOfExperience: 21, 11 | }), 12 | createCook({ 13 | name: "Marilyn Monroe", 14 | yearsOfExperience: 1, 15 | }), 16 | ]; 17 | -------------------------------------------------------------------------------- /examples/enums-and-unions/cook.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, Int, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Cook { 5 | @Field() 6 | name!: string; 7 | 8 | @Field(_type => Int) 9 | yearsOfExperience!: number; 10 | } 11 | -------------------------------------------------------------------------------- /examples/enums-and-unions/difficulty.enum.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from "type-graphql"; 2 | 3 | export enum Difficulty { 4 | Beginner, 5 | Easy, 6 | Medium, 7 | Hard, 8 | MasterChef, 9 | } 10 | 11 | registerEnumType(Difficulty, { 12 | name: "Difficulty", 13 | description: "All possible preparation difficulty levels", 14 | }); 15 | -------------------------------------------------------------------------------- /examples/enums-and-unions/examples.graphql: -------------------------------------------------------------------------------- 1 | query AllRecipes { 2 | recipes { 3 | title 4 | description 5 | preparationDifficulty 6 | cook { 7 | name 8 | } 9 | } 10 | } 11 | 12 | query EasyRecipes { 13 | recipes(difficulty: Easy) { 14 | title 15 | description 16 | ingredients 17 | cook { 18 | name 19 | } 20 | } 21 | } 22 | 23 | query SearchByCookName { 24 | search(cookName: "Gordon") { 25 | __typename 26 | ... on Recipe { 27 | title 28 | preparationDifficulty 29 | cook { 30 | name 31 | } 32 | } 33 | ... on Cook { 34 | name 35 | yearsOfExperience 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/enums-and-unions/index.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import path from "node:path"; 3 | import { ApolloServer } from "@apollo/server"; 4 | import { startStandaloneServer } from "@apollo/server/standalone"; 5 | import { buildSchema } from "type-graphql"; 6 | import { ExampleResolver } from "./resolver"; 7 | 8 | async function bootstrap() { 9 | // Build TypeGraphQL executable schema 10 | const schema = await buildSchema({ 11 | // Array of resolvers 12 | resolvers: [ExampleResolver], 13 | // Create 'schema.graphql' file with schema definition in current directory 14 | emitSchemaFile: path.resolve(__dirname, "schema.graphql"), 15 | }); 16 | 17 | // Create GraphQL server 18 | const server = new ApolloServer({ schema }); 19 | 20 | // Start server 21 | const { url } = await startStandaloneServer(server, { listen: { port: 4000 } }); 22 | console.log(`GraphQL server ready at ${url}`); 23 | } 24 | 25 | bootstrap().catch(console.error); 26 | -------------------------------------------------------------------------------- /examples/enums-and-unions/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | import { Cook } from "./cook.type"; 3 | import { Difficulty } from "./difficulty.enum"; 4 | 5 | @ObjectType() 6 | export class Recipe { 7 | @Field() 8 | title!: string; 9 | 10 | @Field({ nullable: true }) 11 | description?: string; 12 | 13 | @Field(_type => [String]) 14 | ingredients!: string[]; 15 | 16 | @Field(_type => Difficulty) 17 | preparationDifficulty!: Difficulty; 18 | 19 | @Field() 20 | cook!: Cook; 21 | } 22 | -------------------------------------------------------------------------------- /examples/enums-and-unions/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Cook { 7 | name: String! 8 | yearsOfExperience: Int! 9 | } 10 | 11 | """ 12 | All possible preparation difficulty levels 13 | """ 14 | enum Difficulty { 15 | Beginner 16 | Easy 17 | Hard 18 | MasterChef 19 | Medium 20 | } 21 | 22 | type Query { 23 | recipes(difficulty: Difficulty): [Recipe!]! 24 | search(cookName: String!): [SearchResult!]! 25 | } 26 | 27 | type Recipe { 28 | cook: Cook! 29 | description: String 30 | ingredients: [String!]! 31 | preparationDifficulty: Difficulty! 32 | title: String! 33 | } 34 | 35 | union SearchResult = Cook | Recipe 36 | -------------------------------------------------------------------------------- /examples/enums-and-unions/search-result.union.ts: -------------------------------------------------------------------------------- 1 | import { createUnionType } from "type-graphql"; 2 | import { Cook } from "./cook.type"; 3 | import { Recipe } from "./recipe.type"; 4 | 5 | export const SearchResult = createUnionType({ 6 | name: "SearchResult", 7 | types: () => [Recipe, Cook] as const, 8 | }); 9 | -------------------------------------------------------------------------------- /examples/extensions/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type User } from "./user.type"; 2 | 3 | export interface Context { 4 | user?: User; 5 | } 6 | -------------------------------------------------------------------------------- /examples/extensions/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes { 3 | title 4 | description 5 | ingredients 6 | averageRating 7 | ratings 8 | } 9 | } 10 | 11 | mutation AddRecipe { 12 | addRecipe(title: "Sample Recipe") { 13 | averageRating 14 | } 15 | } 16 | 17 | mutation DeleteRecipe { 18 | deleteRecipe(title: "Recipe 1") 19 | } 20 | -------------------------------------------------------------------------------- /examples/extensions/helpers/config.extractors.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type GraphQLFieldConfig, 3 | type GraphQLObjectTypeConfig, 4 | type GraphQLResolveInfo, 5 | } from "graphql"; 6 | 7 | export const extractFieldConfig = (info: GraphQLResolveInfo): GraphQLFieldConfig => { 8 | const { type, extensions, description, deprecationReason } = 9 | info.parentType.getFields()[info.fieldName]; 10 | 11 | return { 12 | type, 13 | description, 14 | extensions, 15 | deprecationReason, 16 | }; 17 | }; 18 | 19 | export const extractParentTypeConfig = ( 20 | info: GraphQLResolveInfo, 21 | ): GraphQLObjectTypeConfig => info.parentType.toConfig(); 22 | -------------------------------------------------------------------------------- /examples/extensions/log-message.decorator.ts: -------------------------------------------------------------------------------- 1 | import { Extensions } from "type-graphql"; 2 | 3 | interface LogOptions { 4 | message: string; 5 | level?: number; 6 | } 7 | 8 | export function LogMessage(messageOrOptions: string | LogOptions) { 9 | // Parse the parameters of the custom decorator 10 | const log: LogOptions = 11 | typeof messageOrOptions === "string" 12 | ? { 13 | level: 4, 14 | message: messageOrOptions, 15 | } 16 | : messageOrOptions; 17 | 18 | // Return the '@Extensions' decorator with a prepared property 19 | return Extensions({ log }); 20 | } 21 | -------------------------------------------------------------------------------- /examples/extensions/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Service } from "typedi"; 2 | 3 | @Service() 4 | export class Logger { 5 | log(...args: unknown[]) { 6 | console.log(...args); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/extensions/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial): Recipe { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export const sampleRecipes = [ 8 | createRecipe({ 9 | title: "Recipe 1", 10 | description: "Desc 1", 11 | ingredients: ["one", "two", "three"], 12 | ratings: [3, 4, 5, 5, 5], 13 | }), 14 | createRecipe({ 15 | title: "Recipe 2", 16 | description: "Desc 2", 17 | ingredients: ["four", "five", "six"], 18 | ratings: [3, 4, 5, 3, 2], 19 | }), 20 | createRecipe({ 21 | title: "Recipe 3", 22 | ingredients: ["seven", "eight", "nine"], 23 | ratings: [4, 4, 5, 5, 4], 24 | }), 25 | ]; 26 | -------------------------------------------------------------------------------- /examples/extensions/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | addRecipe(description: String, title: String!): Recipe! 8 | deleteRecipe(title: String!): Boolean! 9 | } 10 | 11 | type Query { 12 | recipes: [Recipe!]! 13 | } 14 | 15 | type Recipe { 16 | averageRating: Float 17 | description: String 18 | ingredients: [String!]! 19 | ratings: [Int!]! 20 | title: String! 21 | } 22 | -------------------------------------------------------------------------------- /examples/extensions/user.type.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | name: string; 4 | } 5 | -------------------------------------------------------------------------------- /examples/generic-types/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes(first: 3) { 3 | items { 4 | title 5 | ratings 6 | } 7 | total 8 | hasMore 9 | } 10 | } 11 | 12 | mutation AddRecipe { 13 | addSampleRecipe { 14 | title 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/generic-types/index.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import path from "node:path"; 3 | import { ApolloServer } from "@apollo/server"; 4 | import { startStandaloneServer } from "@apollo/server/standalone"; 5 | import { buildSchema } from "type-graphql"; 6 | import { RecipeResolver } from "./recipe.resolver"; 7 | 8 | async function bootstrap() { 9 | // Build TypeGraphQL executable schema 10 | const schema = await buildSchema({ 11 | // Array of resolvers 12 | resolvers: [RecipeResolver], 13 | // Create 'schema.graphql' file with schema definition in current directory 14 | emitSchemaFile: path.resolve(__dirname, "schema.graphql"), 15 | }); 16 | 17 | // Create GraphQL server 18 | const server = new ApolloServer({ schema }); 19 | 20 | // Start server 21 | const { url } = await startStandaloneServer(server, { listen: { port: 4000 } }); 22 | console.log(`GraphQL server ready at ${url}`); 23 | } 24 | 25 | bootstrap().catch(console.error); 26 | -------------------------------------------------------------------------------- /examples/generic-types/paginated-response.type.ts: -------------------------------------------------------------------------------- 1 | import { type ClassType, Field, Int, ObjectType } from "type-graphql"; 2 | 3 | export function PaginatedResponse( 4 | itemsFieldValue: ClassType | string | number | boolean, 5 | ) { 6 | @ObjectType() 7 | abstract class PaginatedResponseClass { 8 | @Field(_type => [itemsFieldValue]) 9 | items!: TItemsFieldValue[]; 10 | 11 | @Field(_type => Int) 12 | total!: number; 13 | 14 | @Field() 15 | hasMore!: boolean; 16 | } 17 | 18 | return PaginatedResponseClass; 19 | } 20 | -------------------------------------------------------------------------------- /examples/generic-types/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { type Recipe } from "./recipe.type"; 2 | 3 | export function createSampleRecipes(): Recipe[] { 4 | return [ 5 | { 6 | description: "Desc 1", 7 | title: "Recipe 1", 8 | ratings: [0, 3, 1], 9 | }, 10 | { 11 | description: "Desc 2", 12 | title: "Recipe 2", 13 | ratings: [4, 2, 3, 1], 14 | }, 15 | { 16 | description: "Desc 3", 17 | title: "Recipe 3", 18 | ratings: [5, 4], 19 | }, 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /examples/generic-types/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, Int, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field() 6 | title!: string; 7 | 8 | @Field({ nullable: true }) 9 | description?: string; 10 | 11 | @Field(_type => [Int]) 12 | ratings!: number[]; 13 | } 14 | -------------------------------------------------------------------------------- /examples/generic-types/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | addSampleRecipe: Recipe! 8 | } 9 | 10 | type Query { 11 | recipes(first: Int = 10): RecipesResponse! 12 | } 13 | 14 | type Recipe { 15 | description: String 16 | ratings: [Int!]! 17 | title: String! 18 | } 19 | 20 | type RecipesResponse { 21 | hasMore: Boolean! 22 | items: [Recipe!]! 23 | total: Int! 24 | } 25 | -------------------------------------------------------------------------------- /examples/graphql-scalars/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipe1 { 2 | recipe(title: "Recipe 1") { 3 | title 4 | description 5 | ratings 6 | creationDate 7 | ratingsCount(minRate: 2) 8 | averageRating 9 | } 10 | } 11 | 12 | query GetRecipes { 13 | recipes { 14 | title 15 | description 16 | creationDate 17 | averageRating 18 | } 19 | } 20 | 21 | mutation AddRecipe { 22 | addRecipe(recipe: { title: "New recipe", description: "Simple description" }) { 23 | creationDate 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/graphql-scalars/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial) { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export function createRecipeSamples() { 8 | return [ 9 | createRecipe({ 10 | description: "Desc 1", 11 | title: "Recipe 1", 12 | ratings: [0, 3, 1], 13 | creationDate: new Date("2018-04-11"), 14 | }), 15 | createRecipe({ 16 | description: "Desc 2", 17 | title: "Recipe 2", 18 | ratings: [4, 2, 3, 1], 19 | creationDate: new Date("2018-04-15"), 20 | }), 21 | createRecipe({ 22 | description: "Desc 3", 23 | title: "Recipe 3", 24 | ratings: [5, 4], 25 | creationDate: new Date(), 26 | }), 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /examples/graphql-scalars/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLNonEmptyString } from "graphql-scalars"; 2 | import { Field, InputType } from "type-graphql"; 3 | import { type Recipe } from "./recipe.type"; 4 | 5 | @InputType() 6 | export class RecipeInput implements Partial { 7 | @Field(_type => GraphQLNonEmptyString) 8 | title!: string; 9 | 10 | @Field(_type => GraphQLNonEmptyString, { nullable: true }) 11 | description?: string; 12 | } 13 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/employee/employee.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { PersonInput } from "../person"; 3 | 4 | @InputType() 5 | export class EmployeeInput extends PersonInput { 6 | @Field() 7 | companyName!: string; 8 | } 9 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/employee/employee.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | import { Person } from "../person"; 3 | 4 | @ObjectType() 5 | export class Employee extends Person { 6 | @Field() 7 | companyName!: string; 8 | } 9 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/employee/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./employee.input"; 2 | export * from "./employee.type"; 3 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetPersons { 2 | persons { 3 | __typename 4 | id 5 | name 6 | age 7 | ... on Student { 8 | universityName 9 | } 10 | ... on Employee { 11 | companyName 12 | } 13 | } 14 | } 15 | 16 | mutation AddStudent { 17 | addStudent( 18 | input: { name: "Student 1", dateOfBirth: "1991-11-30T00:00:00.000Z", universityName: "Uni 1" } 19 | ) { 20 | id 21 | age 22 | } 23 | } 24 | 25 | mutation AddEmployee { 26 | addEmployee( 27 | input: { name: "Employee 1", dateOfBirth: "1995-07-23T00:00:00.000Z", companyName: "Company 1" } 28 | ) { 29 | id 30 | age 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/helpers.ts: -------------------------------------------------------------------------------- 1 | import crypto from "node:crypto"; 2 | 3 | export function getId(): string { 4 | const randomNumber = Math.random(); 5 | const hash = crypto.createHash("sha256"); 6 | hash.update(randomNumber.toString()); 7 | return hash.digest("hex"); 8 | } 9 | 10 | export function calculateAge(birthday: Date) { 11 | const ageDiffMs = Date.now() - birthday.getTime(); 12 | const ageDate = new Date(ageDiffMs); // Milliseconds from epoch 13 | return Math.abs(ageDate.getUTCFullYear() - 1970); 14 | } 15 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/person/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./person.input"; 2 | export * from "./person.interface"; 3 | export * from "./person.type"; 4 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/person/person.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | 3 | @InputType() 4 | export class PersonInput { 5 | @Field() 6 | name!: string; 7 | 8 | @Field() 9 | dateOfBirth!: Date; 10 | } 11 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/person/person.interface.ts: -------------------------------------------------------------------------------- 1 | import { Arg, Field, ID, Int, InterfaceType } from "type-graphql"; 2 | import { type IResource } from "../resource"; 3 | 4 | @InterfaceType({ 5 | // Workaround issue #373 (https://github.com/MichalLytek/type-graphql/issues/373) 6 | resolveType: value => value.constructor.name, 7 | }) 8 | export abstract class IPerson implements IResource { 9 | @Field(_type => ID) 10 | id!: string; 11 | 12 | @Field() 13 | name!: string; 14 | 15 | @Field(_type => Int) 16 | age!: number; 17 | 18 | @Field() 19 | avatar(@Arg("size") _size: number): string { 20 | throw new Error("Method not implemented."); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/person/person.type.ts: -------------------------------------------------------------------------------- 1 | import { Arg, Field, ObjectType } from "type-graphql"; 2 | import { IPerson } from "./person.interface"; 3 | 4 | @ObjectType({ implements: IPerson }) 5 | export class Person implements IPerson { 6 | id!: string; 7 | 8 | name!: string; 9 | 10 | age!: number; 11 | 12 | @Field() 13 | avatar(@Arg("size") size: number): string { 14 | return `http://i.pravatar.cc/${size}`; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/resource/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resource.interface"; 2 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/resource/resource.interface.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, InterfaceType } from "type-graphql"; 2 | 3 | @InterfaceType() 4 | export abstract class IResource { 5 | @Field(_type => ID) 6 | id!: string; 7 | } 8 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/student/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./student.input"; 2 | export * from "./student.type"; 3 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/student/student.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { PersonInput } from "../person"; 3 | 4 | @InputType() 5 | export class StudentInput extends PersonInput { 6 | @Field() 7 | universityName!: string; 8 | } 9 | -------------------------------------------------------------------------------- /examples/interfaces-inheritance/student/student.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | import { Person } from "../person"; 3 | 4 | @ObjectType() 5 | export class Student extends Person { 6 | @Field() 7 | universityName!: string; 8 | } 9 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type User } from "./user.type"; 2 | 3 | export interface Context { 4 | currentUser: User; 5 | } 6 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/decorators/current-user.ts: -------------------------------------------------------------------------------- 1 | import { createParameterDecorator } from "type-graphql"; 2 | import { type Context } from "../context.type"; 3 | 4 | export function CurrentUser() { 5 | return createParameterDecorator(({ context }) => context.currentUser); 6 | } 7 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./current-user"; 2 | export * from "./validate-args"; 3 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/decorators/random-id-arg.ts: -------------------------------------------------------------------------------- 1 | import { Int, createParameterDecorator } from "type-graphql"; 2 | 3 | const MAX_ID_VALUE = 3; // Number.MAX_SAFE_INTEGER 4 | 5 | export function RandomIdArg(argName = "id") { 6 | return createParameterDecorator( 7 | ({ args }) => args[argName] ?? Math.round(Math.random() * MAX_ID_VALUE), 8 | { 9 | arg: { 10 | name: argName, 11 | typeFunc: () => Int, 12 | options: { 13 | nullable: true, 14 | description: "Accepts provided id or generates a random one.", 15 | validateFn: (value: number): void => { 16 | if (value < 0 || value > MAX_ID_VALUE) { 17 | throw new Error(`Invalid value for ${argName}`); 18 | } 19 | }, 20 | }, 21 | }, 22 | }, 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/decorators/validate-args.ts: -------------------------------------------------------------------------------- 1 | import { validate } from "class-validator"; 2 | import { 3 | ArgumentValidationError, 4 | type ClassType, 5 | createMethodMiddlewareDecorator, 6 | } from "type-graphql"; 7 | 8 | // Sample implementation of custom validation decorator 9 | // This example use 'class-validator' however you can plug-in 'joi' or any other validation library 10 | export function ValidateArgs(Type: ClassType) { 11 | return createMethodMiddlewareDecorator(async ({ args }, next) => { 12 | const instance = Object.assign(new Type(), args); 13 | const validationErrors = await validate(instance); 14 | if (validationErrors.length > 0) { 15 | throw new ArgumentValidationError(validationErrors); 16 | } 17 | return next(); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/examples.graphql: -------------------------------------------------------------------------------- 1 | query InvalidArgs { 2 | recipes(take: -1) { 3 | title 4 | description 5 | } 6 | } 7 | 8 | query LoggingQuery { 9 | recipes { 10 | title 11 | description 12 | ratings 13 | } 14 | } 15 | 16 | query InterceptorsQuery { 17 | recipes(skip: 1, take: 2) { 18 | title 19 | ratings 20 | averageRating 21 | } 22 | } 23 | 24 | query RandomIdQuery { 25 | recipe { 26 | id 27 | title 28 | averageRating 29 | description 30 | } 31 | } 32 | 33 | query SelectedIdQuery { 34 | recipe(id: 2) { 35 | id 36 | title 37 | averageRating 38 | description 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/logger.ts: -------------------------------------------------------------------------------- 1 | import { Service } from "typedi"; 2 | 3 | @Service() 4 | export class Logger { 5 | log(...args: any[]) { 6 | // Replace with a more sophisticated solution 7 | console.log(...args); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./error-logger"; 2 | export * from "./log-access"; 3 | export * from "./number-interceptor"; 4 | export * from "./resolve-time"; 5 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/middlewares/log-access.ts: -------------------------------------------------------------------------------- 1 | import { type MiddlewareInterface, type NextFn, type ResolverData } from "type-graphql"; 2 | import { Service } from "typedi"; 3 | import { type Context } from "../context.type"; 4 | import { Logger } from "../logger"; 5 | 6 | @Service() 7 | export class LogAccessMiddleware implements MiddlewareInterface { 8 | constructor(private readonly logger: Logger) {} 9 | 10 | async use({ context, info }: ResolverData, next: NextFn) { 11 | this.logger.log( 12 | `Logging access: ${context.currentUser.name} -> ${info.parentType.name}.${info.fieldName}`, 13 | ); 14 | return next(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/middlewares/number-interceptor.ts: -------------------------------------------------------------------------------- 1 | import { type MiddlewareFn } from "type-graphql"; 2 | 3 | export function NumberInterceptor(minValue: number): MiddlewareFn { 4 | return async (_, next) => { 5 | const result = await next(); 6 | // Hide ratings below minValue 7 | if (typeof result === "number" && result < minValue) { 8 | return null; 9 | } 10 | return result; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/middlewares/resolve-time.ts: -------------------------------------------------------------------------------- 1 | import { type MiddlewareFn } from "type-graphql"; 2 | 3 | export const ResolveTimeMiddleware: MiddlewareFn = async ({ info }, next) => { 4 | const start = Date.now(); 5 | await next(); 6 | const resolveTime = Date.now() - start; 7 | console.log(`${info.parentType.name}.${info.fieldName} [${resolveTime} ms]`); 8 | }; 9 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/recipe/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./recipe.args"; 2 | export * from "./recipe.resolver"; 3 | export * from "./recipe.data"; 4 | export * from "./recipe.type"; 5 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/recipe/recipe.args.ts: -------------------------------------------------------------------------------- 1 | import { Max, Min } from "class-validator"; 2 | import { ArgsType, Field, Int } from "type-graphql"; 3 | 4 | @ArgsType() 5 | export class RecipesArgs { 6 | @Field(_type => Int) 7 | @Min(0) 8 | skip = 0; 9 | 10 | @Field(_type => Int) 11 | @Min(1) 12 | @Max(50) 13 | take = 10; 14 | } 15 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/recipe/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | let lastRecipeId = 0; 4 | 5 | function createRecipe(recipeData: Partial): Recipe { 6 | return Object.assign(new Recipe(), { 7 | // eslint-disable-next-line no-plusplus 8 | id: lastRecipeId++, 9 | ...recipeData, 10 | }); 11 | } 12 | 13 | export const recipes = [ 14 | createRecipe({ 15 | description: "Desc 1", 16 | title: "Recipe 1", 17 | ratings: [0, 3, 1], 18 | }), 19 | createRecipe({ 20 | description: "Desc 2", 21 | title: "Recipe 2", 22 | ratings: [4, 2, 3, 1], 23 | }), 24 | createRecipe({ 25 | description: "Desc 3", 26 | title: "Recipe 3", 27 | ratings: [4, 5, 3, 1, 5], 28 | }), 29 | ]; 30 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/recipe/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, Float, Int, ObjectType, UseMiddleware } from "type-graphql"; 2 | import { LogAccessMiddleware, NumberInterceptor } from "../middlewares"; 3 | 4 | @ObjectType() 5 | export class Recipe { 6 | @Field(_type => Int) 7 | id!: number; 8 | 9 | @Field() 10 | title!: string; 11 | 12 | @Field({ nullable: true }) 13 | description?: string; 14 | 15 | @Field(_type => [Int]) 16 | @UseMiddleware(LogAccessMiddleware) 17 | ratings!: number[]; 18 | 19 | @Field(_type => Float, { nullable: true }) 20 | @UseMiddleware(NumberInterceptor(3)) 21 | get averageRating(): number | null { 22 | const ratingsCount = this.ratings.length; 23 | if (ratingsCount === 0) { 24 | return null; 25 | } 26 | const ratingsSum = this.ratings.reduce((a, b) => a + b, 0); 27 | return ratingsSum / ratingsCount; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Query { 7 | recipe( 8 | """ 9 | Accepts provided id or generates a random one. 10 | """ 11 | id: Int 12 | ): Recipe 13 | recipes(skip: Int! = 0, take: Int! = 10): [Recipe!]! 14 | } 15 | 16 | type Recipe { 17 | averageRating: Float 18 | description: String 19 | id: Int! 20 | ratings: [Int!]! 21 | title: String! 22 | } 23 | -------------------------------------------------------------------------------- /examples/middlewares-custom-decorators/user.type.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id: number; 3 | name: string; 4 | } 5 | -------------------------------------------------------------------------------- /examples/mikro-orm/.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL='postgresql://username:password@localhost:5432/type-graphql-example-mikroorm' 2 | -------------------------------------------------------------------------------- /examples/mikro-orm/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type EntityManager } from "@mikro-orm/core"; 2 | import { type User } from "./entities"; 3 | 4 | export interface Context { 5 | entityManager: EntityManager; 6 | user: User; 7 | } 8 | -------------------------------------------------------------------------------- /examples/mikro-orm/entities/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating"; 2 | export * from "./recipe"; 3 | export * from "./user"; 4 | -------------------------------------------------------------------------------- /examples/mikro-orm/entities/rating.ts: -------------------------------------------------------------------------------- 1 | import { Entity, ManyToOne, OptionalProps, PrimaryKey, Property } from "@mikro-orm/core"; 2 | import { Field, Int, ObjectType } from "type-graphql"; 3 | import { Recipe } from "./recipe"; 4 | import { User } from "./user"; 5 | 6 | @Entity() 7 | @ObjectType() 8 | export class Rating { 9 | @PrimaryKey() 10 | readonly id!: number; 11 | 12 | @Field(_type => Int) 13 | @Property({ type: "smallint" }) 14 | value!: number; 15 | 16 | @Field(_type => User) 17 | @ManyToOne(_type => User) 18 | user!: User; 19 | 20 | @Field() 21 | @Property({ onCreate: () => new Date() }) 22 | date!: Date; 23 | 24 | @ManyToOne(_type => Recipe) 25 | recipe!: Recipe; 26 | 27 | [OptionalProps]?: "date"; 28 | } 29 | -------------------------------------------------------------------------------- /examples/mikro-orm/entities/recipe.ts: -------------------------------------------------------------------------------- 1 | import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from "@mikro-orm/core"; 2 | import { Field, ID, ObjectType } from "type-graphql"; 3 | import { Rating } from "./rating"; 4 | import { User } from "./user"; 5 | 6 | @Entity() 7 | @ObjectType() 8 | export class Recipe { 9 | @Field(_type => ID) 10 | @PrimaryKey() 11 | readonly id!: number; 12 | 13 | @Field() 14 | @Property() 15 | title!: string; 16 | 17 | @Field({ nullable: true }) 18 | @Property({ nullable: true }) 19 | description?: string; 20 | 21 | @Field(_type => [Rating]) 22 | @OneToMany(_type => Rating, rating => rating.recipe) 23 | ratings = new Collection(this); 24 | 25 | @Field(_type => User) 26 | @ManyToOne(_type => User) 27 | author!: User; 28 | } 29 | -------------------------------------------------------------------------------- /examples/mikro-orm/entities/user.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryKey, Property } from "@mikro-orm/core"; 2 | import { Field, ID, ObjectType } from "type-graphql"; 3 | 4 | @ObjectType() 5 | @Entity() 6 | export class User { 7 | @Field(_type => ID) 8 | @PrimaryKey() 9 | readonly id!: number; 10 | 11 | @Field() 12 | @Property() 13 | email!: string; 14 | 15 | @Field({ nullable: true }) 16 | @Property({ nullable: true }) 17 | nickname?: string; 18 | 19 | @Property() 20 | password!: string; 21 | } 22 | -------------------------------------------------------------------------------- /examples/mikro-orm/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes { 3 | id 4 | title 5 | author { 6 | email 7 | } 8 | ratings { 9 | value 10 | } 11 | } 12 | } 13 | 14 | query GetRecipe { 15 | recipe(recipeId: 1) { 16 | id 17 | title 18 | ratings { 19 | value 20 | user { 21 | nickname 22 | } 23 | date 24 | } 25 | author { 26 | id 27 | nickname 28 | email 29 | } 30 | } 31 | } 32 | 33 | mutation AddRecipe { 34 | addRecipe(recipe: { title: "New Recipe" }) { 35 | id 36 | ratings { 37 | value 38 | } 39 | author { 40 | nickname 41 | } 42 | } 43 | } 44 | 45 | mutation RatingRecipe { 46 | rating(rating: { recipeId: 3, value: 4 }) { 47 | id 48 | ratings { 49 | value 50 | user { 51 | email 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/mikro-orm/resolvers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.resolver"; 2 | export * from "./recipe.resolver"; 3 | -------------------------------------------------------------------------------- /examples/mikro-orm/resolvers/rating.resolver.ts: -------------------------------------------------------------------------------- 1 | import { Ctx, FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Context } from "../context.type"; 3 | import { Rating, User } from "../entities"; 4 | 5 | @Resolver(_of => Rating) 6 | export class RatingResolver { 7 | @FieldResolver() 8 | async user(@Root() rating: Rating, @Ctx() { entityManager }: Context): Promise { 9 | return entityManager.findOneOrFail(User, rating.user.id); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/mikro-orm/resolvers/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.input"; 2 | export * from "./recipe.input"; 3 | -------------------------------------------------------------------------------- /examples/mikro-orm/resolvers/types/rating.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType, Int } from "type-graphql"; 2 | 3 | @InputType() 4 | export class RatingInput { 5 | @Field(_type => Int) 6 | recipeId!: number; 7 | 8 | @Field(_type => Int) 9 | value!: number; 10 | } 11 | -------------------------------------------------------------------------------- /examples/mikro-orm/resolvers/types/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "../../entities"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | } 12 | -------------------------------------------------------------------------------- /examples/mixin-classes/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetUsers { 2 | users { 3 | ...UserInfo 4 | } 5 | } 6 | 7 | mutation CreateUser { 8 | createUser( 9 | input: { 10 | forename: "Sample 1" 11 | dateOfBirth: "2000-09-21T14:54:17.369Z" 12 | email: "test@test.test" 13 | password: "qwerty123" 14 | } 15 | ) { 16 | ...UserInfo 17 | } 18 | } 19 | mutation AmendUser { 20 | amendUser( 21 | input: { 22 | id: 1 23 | forename: "Sample Amend 1" 24 | dateOfBirth: "2000-09-21T14:54:17.369Z" 25 | email: "test2@test.test" 26 | } 27 | ) { 28 | ...UserInfo 29 | } 30 | } 31 | 32 | fragment UserInfo on User { 33 | id 34 | forename 35 | surname 36 | dateOfBirth 37 | email 38 | } 39 | -------------------------------------------------------------------------------- /examples/mixin-classes/inputs/amend.user.input.ts: -------------------------------------------------------------------------------- 1 | import { InputType } from "type-graphql"; 2 | import { withId } from "../mixins"; 3 | import { UserDetails } from "../types"; 4 | 5 | // 'AmendUser' is like the full 'User' class without the password 6 | @InputType() 7 | export class AmendUserInput extends withId(UserDetails) {} 8 | -------------------------------------------------------------------------------- /examples/mixin-classes/inputs/create.user.input.ts: -------------------------------------------------------------------------------- 1 | import { InputType } from "type-graphql"; 2 | import { withPassword } from "../mixins"; 3 | import { UserDetails } from "../types"; 4 | 5 | // 'CreateUser' is like the full 'User' class without the id 6 | @InputType() 7 | export class CreateUserInput extends withPassword(UserDetails) {} 8 | -------------------------------------------------------------------------------- /examples/mixin-classes/inputs/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./amend.user.input"; 2 | export * from "./create.user.input"; 3 | -------------------------------------------------------------------------------- /examples/mixin-classes/mixins/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./with.id"; 2 | export * from "./with.password"; 3 | -------------------------------------------------------------------------------- /examples/mixin-classes/mixins/with.id.ts: -------------------------------------------------------------------------------- 1 | import { type ClassType, Field, InputType, Int, ObjectType } from "type-graphql"; 2 | 3 | // Adds 'id' property to the base, extended class 4 | export function withId(BaseClass: TClassType) { 5 | @ObjectType() 6 | @InputType() 7 | class IDTrait extends BaseClass { 8 | @Field(_type => Int) 9 | id!: number; 10 | } 11 | 12 | return IDTrait; 13 | } 14 | -------------------------------------------------------------------------------- /examples/mixin-classes/mixins/with.password.ts: -------------------------------------------------------------------------------- 1 | import { MinLength } from "class-validator"; 2 | import { type ClassType, Field, InputType, ObjectType } from "type-graphql"; 3 | 4 | // Adds 'password' property with validation to the base, extended class 5 | export function withPassword(BaseClass: TClassType) { 6 | @ObjectType() 7 | @InputType() 8 | class PasswordTrait extends BaseClass { 9 | @MinLength(8) 10 | @Field() 11 | password!: string; 12 | } 13 | 14 | return PasswordTrait; 15 | } 16 | -------------------------------------------------------------------------------- /examples/mixin-classes/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./user"; 2 | export * from "./user.details"; 3 | -------------------------------------------------------------------------------- /examples/mixin-classes/types/user.details.ts: -------------------------------------------------------------------------------- 1 | import { IsEmail } from "class-validator"; 2 | import { Field, InputType, ObjectType } from "type-graphql"; 3 | 4 | // 'UserDetails' stores base common user properties 5 | @ObjectType() 6 | @InputType("UserDetailsInput") 7 | export class UserDetails { 8 | @Field() 9 | forename!: string; 10 | 11 | @Field({ nullable: true }) 12 | surname?: string; 13 | 14 | @Field(_type => Date) 15 | dateOfBirth!: Date; 16 | 17 | @IsEmail() 18 | @Field() 19 | email!: string; 20 | } 21 | -------------------------------------------------------------------------------- /examples/mixin-classes/types/user.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from "type-graphql"; 2 | import { UserDetails } from "./user.details"; 3 | import { withId } from "../mixins/with.id"; 4 | 5 | // 'User' is a full object with 'id' and hidden 'password' 6 | @ObjectType() 7 | export class User extends withId(UserDetails) { 8 | // No TypeGraphQL decorator, hidden in schema 9 | password!: string; 10 | } 11 | -------------------------------------------------------------------------------- /examples/query-complexity/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipesWithComplexityError { 2 | recipes(count: 3) { 3 | title 4 | averageRating 5 | } 6 | } 7 | 8 | query GetRecipesWithoutComplexityError { 9 | recipes(count: 2) { 10 | title 11 | ratingsCount 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/query-complexity/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial): Recipe { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export function createRecipeSamples() { 8 | return [ 9 | createRecipe({ 10 | title: "Recipe 1", 11 | ratings: [0, 3, 1], 12 | }), 13 | createRecipe({ 14 | title: "Recipe 2", 15 | ratings: [4, 2, 3, 1], 16 | }), 17 | createRecipe({ 18 | title: "Recipe 3", 19 | ratings: [5, 4], 20 | }), 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /examples/query-complexity/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, Float, Int, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | /* By default, every field gets a complexity of 1 */ 6 | @Field() 7 | title!: string; 8 | 9 | /* Which can be customized by passing the complexity parameter */ 10 | @Field(_type => Int, { complexity: 2 }) 11 | ratingsCount!: number; 12 | 13 | @Field(_type => Float, { 14 | nullable: true, 15 | complexity: 10, 16 | }) 17 | get averageRating(): number | null { 18 | const ratingsCount = this.ratings.length; 19 | if (ratingsCount === 0) { 20 | return null; 21 | } 22 | const ratingsSum = this.ratings.reduce((a, b) => a + b, 0); 23 | return ratingsSum / ratingsCount; 24 | } 25 | 26 | // Internal property, not exposed in schema 27 | ratings!: number[]; 28 | } 29 | -------------------------------------------------------------------------------- /examples/query-complexity/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Query { 7 | recipes(count: Float!): [Recipe!]! 8 | } 9 | 10 | type Recipe { 11 | averageRating: Float 12 | ratingsCount: Int! 13 | title: String! 14 | } 15 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/.env.example: -------------------------------------------------------------------------------- 1 | REDIS_URL="redis://username:password@localhost:6379/0" 2 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/comment.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, InputType } from "type-graphql"; 2 | import { type Comment } from "./comment.type"; 3 | 4 | @InputType() 5 | export class CommentInput implements Partial { 6 | @Field(_type => ID) 7 | recipeId!: string; 8 | 9 | @Field({ nullable: true }) 10 | nickname?: string; 11 | 12 | @Field() 13 | content!: string; 14 | } 15 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/comment.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Comment { 5 | @Field({ nullable: true }) 6 | nickname?: string; 7 | 8 | @Field() 9 | content!: string; 10 | 11 | @Field() 12 | date!: Date; 13 | } 14 | 15 | export interface NewCommentPayload { 16 | recipeId: string; 17 | 18 | dateString: string; // Limitation of Redis payload serialization 19 | 20 | content: string; 21 | 22 | nickname?: string; 23 | } 24 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/examples.graphql: -------------------------------------------------------------------------------- 1 | query FirstRecipe { 2 | recipe(id: "1") { 3 | title 4 | description 5 | comments { 6 | nickname 7 | content 8 | date 9 | } 10 | } 11 | } 12 | 13 | mutation AddCommentToRecipe1 { 14 | addNewComment(comment: { recipeId: "1", nickname: "MichalLytek", content: "Nice one!" }) 15 | } 16 | 17 | mutation AddCommentToRecipe2 { 18 | addNewComment(comment: { recipeId: "2", nickname: "MichalLytek", content: "Nice two!" }) 19 | } 20 | 21 | subscription NewCommentsForRecipe2 { 22 | newComments(recipeId: "2") { 23 | nickname 24 | content 25 | date 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/pubsub.ts: -------------------------------------------------------------------------------- 1 | import { createRedisEventTarget } from "@graphql-yoga/redis-event-target"; 2 | import { createPubSub } from "@graphql-yoga/subscription"; 3 | import { Redis } from "ioredis"; 4 | import { type NewCommentPayload } from "./comment.type"; 5 | 6 | const redisUrl = process.env.REDIS_URL; 7 | if (!redisUrl) { 8 | throw new Error("REDIS_URL env variable is not defined"); 9 | } 10 | 11 | export const enum Topic { 12 | NEW_COMMENT = "NEW_COMMENT", 13 | } 14 | 15 | export const pubSub = createPubSub<{ 16 | [Topic.NEW_COMMENT]: [NewCommentPayload]; 17 | }>({ 18 | eventTarget: createRedisEventTarget({ 19 | publishClient: new Redis(redisUrl, { 20 | retryStrategy: times => Math.max(times * 100, 3000), 21 | }), 22 | subscribeClient: new Redis(redisUrl, { 23 | retryStrategy: times => Math.max(times * 100, 3000), 24 | }), 25 | }), 26 | }); 27 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/recipe.resolver.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field, ID } from "type-graphql"; 2 | 3 | @ArgsType() 4 | export class NewCommentsArgs { 5 | @Field(_type => ID) 6 | recipeId!: string; 7 | } 8 | -------------------------------------------------------------------------------- /examples/redis-subscriptions/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | import { Comment } from "./comment.type"; 3 | 4 | @ObjectType() 5 | export class Recipe { 6 | @Field(_type => ID) 7 | id!: string; 8 | 9 | @Field() 10 | title!: string; 11 | 12 | @Field({ nullable: true }) 13 | description?: string; 14 | 15 | @Field(_type => [Comment]) 16 | comments!: Comment[]; 17 | } 18 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/examples.graphql: -------------------------------------------------------------------------------- 1 | query AllPersons { 2 | persons { 3 | id 4 | name 5 | age 6 | role 7 | } 8 | } 9 | 10 | query OneRecipe { 11 | recipe(id: 1) { 12 | uuid 13 | title 14 | ratings 15 | averageRating 16 | } 17 | } 18 | 19 | mutation PromotePersonOne { 20 | promote(personId: 1) 21 | } 22 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/person/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./person.resolver"; 2 | export * from "./person.role"; 3 | export * from "./person.type"; 4 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/person/person.role.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from "type-graphql"; 2 | 3 | export enum PersonRole { 4 | Normal, 5 | Pro, 6 | Admin, 7 | } 8 | 9 | registerEnumType(PersonRole, { name: "PersonRole" }); 10 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/person/person.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, Int, ObjectType } from "type-graphql"; 2 | import { PersonRole } from "./person.role"; 3 | import { type Resource } from "../resource"; 4 | 5 | @ObjectType() 6 | export class Person implements Resource { 7 | @Field() 8 | id!: number; 9 | 10 | @Field() 11 | name!: string; 12 | 13 | @Field(_type => Int) 14 | age!: number; 15 | 16 | @Field(_type => PersonRole) 17 | role!: PersonRole; 18 | } 19 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/recipe/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./recipe.resolver"; 2 | export * from "./recipe.type"; 3 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/recipe/recipe.resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Service } from "typedi"; 3 | import { Recipe } from "./recipe.type"; 4 | import { ResourceResolver } from "../resource"; 5 | 6 | const recipes: Recipe[] = [ 7 | { 8 | id: 1, 9 | title: "Recipe 1", 10 | ratings: [1, 3, 4], 11 | }, 12 | ]; 13 | 14 | @Resolver(_of => Recipe) 15 | @Service() 16 | export class RecipeResolver extends ResourceResolver(Recipe, recipes) { 17 | // Here you can add resource-specific operations 18 | 19 | @FieldResolver() 20 | averageRating(@Root() recipe: Recipe): number { 21 | return recipe.ratings.reduce((a, b) => a + b, 0) / recipe.ratings.length; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/recipe/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, Int, ObjectType } from "type-graphql"; 2 | import { type Resource } from "../resource"; 3 | 4 | @ObjectType() 5 | export class Recipe implements Resource { 6 | @Field() 7 | id!: number; 8 | 9 | @Field() 10 | title!: string; 11 | 12 | @Field(_type => [Int]) 13 | ratings!: number[]; 14 | } 15 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/resource/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resource.args"; 2 | export * from "./resource"; 3 | export * from "./resource.resolver"; 4 | export * from "./resource.service"; 5 | export * from "./resource.service.factory"; 6 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/resource/resource.args.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field, Int } from "type-graphql"; 2 | 3 | @ArgsType() 4 | export class GetAllArgs { 5 | @Field(_type => Int) 6 | skip = 0; 7 | 8 | @Field(_type => Int) 9 | take = 10; 10 | } 11 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/resource/resource.service.factory.ts: -------------------------------------------------------------------------------- 1 | import { Service } from "typedi"; 2 | import { type Resource } from "./resource"; 3 | import { ResourceService } from "./resource.service"; 4 | 5 | // Use factory to separate instance of service for each generic 6 | @Service() 7 | export class ResourceServiceFactory { 8 | create(resources?: TResource[]) { 9 | return new ResourceService(resources); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/resource/resource.service.ts: -------------------------------------------------------------------------------- 1 | import { type Resource } from "./resource"; 2 | 3 | export class ResourceService { 4 | constructor(protected resources: TResource[] = []) {} 5 | 6 | getOne(id: number): TResource | undefined { 7 | return this.resources.find(res => res.id === id); 8 | } 9 | 10 | getAll(skip: number, take: number): TResource[] { 11 | const start: number = skip; 12 | const end: number = skip + take; 13 | return this.resources.slice(start, end); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/resource/resource.ts: -------------------------------------------------------------------------------- 1 | export interface Resource { 2 | id: number; 3 | } 4 | -------------------------------------------------------------------------------- /examples/resolvers-inheritance/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | promote(personId: Int!): Boolean! 8 | } 9 | 10 | type Person { 11 | age: Int! 12 | id: Float! 13 | name: String! 14 | role: PersonRole! 15 | uuid: String! 16 | } 17 | 18 | enum PersonRole { 19 | Admin 20 | Normal 21 | Pro 22 | } 23 | 24 | type Query { 25 | person(id: Int!): Person! 26 | persons(skip: Int! = 0, take: Int! = 10): [Person!]! 27 | recipe(id: Int!): Recipe! 28 | recipes(skip: Int! = 0, take: Int! = 10): [Recipe!]! 29 | } 30 | 31 | type Recipe { 32 | averageRating: Float! 33 | id: Float! 34 | ratings: [Int!]! 35 | title: String! 36 | uuid: String! 37 | } 38 | -------------------------------------------------------------------------------- /examples/simple-subscriptions/examples.graphql: -------------------------------------------------------------------------------- 1 | subscription AllNotifications { 2 | normalSubscription { 3 | id 4 | message 5 | date 6 | } 7 | } 8 | 9 | subscription EvenNotifications { 10 | subscriptionWithFilter { 11 | id 12 | message 13 | date 14 | } 15 | } 16 | 17 | mutation PublishMessage { 18 | pubSubMutation(message: "Hello") 19 | } 20 | 21 | # Dynamic topics 22 | 23 | subscription DynamicTopic { 24 | subscribeToTopicFromArg(topic: "FOO_MESSAGES") { 25 | id 26 | message 27 | } 28 | } 29 | 30 | mutation PublishMessageToDynamicTopic { 31 | publishToDynamicTopic(topic: "FOO_MESSAGES", message: "Hi Foo!") 32 | } 33 | 34 | # Dynamic topic id 35 | 36 | subscription DynamicTopicId { 37 | subscribeToTopicIdFromArg(topicId: 2137) { 38 | id 39 | message 40 | } 41 | } 42 | 43 | mutation PublishMessageToDynamicTopicId { 44 | publishWithDynamicTopicId(topicId: 2137, message: "Hi Foo!") 45 | } 46 | -------------------------------------------------------------------------------- /examples/simple-subscriptions/notification.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Notification { 5 | @Field(_type => ID) 6 | id!: number; 7 | 8 | @Field({ nullable: true }) 9 | message?: string; 10 | 11 | @Field(_type => Date) 12 | date!: Date; 13 | } 14 | 15 | export interface NotificationPayload { 16 | id: number; 17 | 18 | message?: string; 19 | } 20 | -------------------------------------------------------------------------------- /examples/simple-subscriptions/pubsub.ts: -------------------------------------------------------------------------------- 1 | import { createPubSub } from "@graphql-yoga/subscription"; 2 | import { type NotificationPayload } from "./notification.type"; 3 | 4 | export const enum Topic { 5 | NOTIFICATIONS = "NOTIFICATIONS", 6 | DYNAMIC_ID_TOPIC = "DYNAMIC_ID_TOPIC", 7 | } 8 | 9 | export const pubSub = createPubSub< 10 | { 11 | [Topic.NOTIFICATIONS]: [NotificationPayload]; 12 | [Topic.DYNAMIC_ID_TOPIC]: [number, NotificationPayload]; 13 | } & Record // Fallback for dynamic topics 14 | >(); 15 | -------------------------------------------------------------------------------- /examples/simple-usage/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipe1 { 2 | recipe(title: "Recipe 1") { 3 | title 4 | description 5 | ratings 6 | creationDate 7 | ratingsCount(minRate: 2) 8 | averageRating 9 | } 10 | } 11 | 12 | query GetRecipes { 13 | recipes { 14 | title 15 | description 16 | creationDate 17 | averageRating 18 | } 19 | } 20 | 21 | mutation AddRecipe { 22 | addRecipe(recipe: { title: "New recipe", description: "Simple description" }) { 23 | creationDate 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/simple-usage/index.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import path from "node:path"; 3 | import { ApolloServer } from "@apollo/server"; 4 | import { startStandaloneServer } from "@apollo/server/standalone"; 5 | import { buildSchema } from "type-graphql"; 6 | import { RecipeResolver } from "./recipe.resolver"; 7 | 8 | async function bootstrap() { 9 | // Build TypeGraphQL executable schema 10 | const schema = await buildSchema({ 11 | // Array of resolvers 12 | resolvers: [RecipeResolver], 13 | // Create 'schema.graphql' file with schema definition in current directory 14 | emitSchemaFile: path.resolve(__dirname, "schema.graphql"), 15 | }); 16 | 17 | // Create GraphQL server 18 | const server = new ApolloServer({ schema }); 19 | 20 | // Start server 21 | const { url } = await startStandaloneServer(server, { listen: { port: 4000 } }); 22 | console.log(`GraphQL server ready at ${url}`); 23 | } 24 | 25 | bootstrap().catch(console.error); 26 | -------------------------------------------------------------------------------- /examples/simple-usage/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial) { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export function createRecipeSamples() { 8 | return [ 9 | createRecipe({ 10 | description: "Desc 1", 11 | title: "Recipe 1", 12 | ratings: [0, 3, 1], 13 | creationDate: new Date("2018-04-11"), 14 | }), 15 | createRecipe({ 16 | description: "Desc 2", 17 | title: "Recipe 2", 18 | ratings: [4, 2, 3, 1], 19 | creationDate: new Date("2018-04-15"), 20 | }), 21 | createRecipe({ 22 | description: "Desc 3", 23 | title: "Recipe 3", 24 | ratings: [5, 4], 25 | creationDate: new Date(), 26 | }), 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /examples/simple-usage/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "./recipe.type"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | } 12 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.cjs.json", 3 | "include": ["../src", "./"], 4 | "compilerOptions": { 5 | "emitDecoratorMetadata": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/tsyringe/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipe1 { 2 | recipe(recipeId: "1") { 3 | title 4 | description 5 | ingredients 6 | numberInCollection 7 | } 8 | } 9 | 10 | query GetRecipes { 11 | recipes { 12 | title 13 | description 14 | ingredientsLength 15 | numberInCollection 16 | } 17 | } 18 | 19 | mutation AddRecipe { 20 | addRecipe(recipe: { title: "New recipe", ingredients: ["One", "Two", "Three"] }) { 21 | id 22 | numberInCollection 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/tsyringe/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial): Recipe { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export const sampleRecipes = [ 8 | createRecipe({ 9 | id: "1", 10 | title: "Recipe 1", 11 | description: "Desc 1", 12 | ingredients: ["one", "two", "three"], 13 | }), 14 | createRecipe({ 15 | id: "2", 16 | title: "Recipe 2", 17 | description: "Desc 2", 18 | ingredients: ["four", "five", "six"], 19 | }), 20 | createRecipe({ 21 | id: "3", 22 | title: "Recipe 3", 23 | ingredients: ["seven", "eight", "nine"], 24 | }), 25 | ]; 26 | -------------------------------------------------------------------------------- /examples/tsyringe/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "./recipe.type"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | 12 | @Field(_type => [String]) 13 | ingredients!: string[]; 14 | } 15 | -------------------------------------------------------------------------------- /examples/tsyringe/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, Int, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field(_type => ID) 6 | id!: string; 7 | 8 | @Field() 9 | title!: string; 10 | 11 | @Field({ nullable: true }) 12 | description?: string; 13 | 14 | @Field(_type => [String]) 15 | ingredients!: string[]; 16 | 17 | @Field(_type => Int) 18 | protected numberInCollection!: number; 19 | 20 | @Field(_type => Int) 21 | protected get ingredientsLength(): number { 22 | return this.ingredients.length; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/tsyringe/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | addRecipe(recipe: RecipeInput!): Recipe! 8 | } 9 | 10 | type Query { 11 | recipe(recipeId: String!): Recipe 12 | recipes: [Recipe!]! 13 | } 14 | 15 | type Recipe { 16 | description: String 17 | id: ID! 18 | ingredients: [String!]! 19 | ingredientsLength: Int! 20 | numberInCollection: Int! 21 | title: String! 22 | } 23 | 24 | input RecipeInput { 25 | description: String 26 | ingredients: [String!]! 27 | title: String! 28 | } 29 | -------------------------------------------------------------------------------- /examples/typegoose/.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="mongodb://username:password@localhost:27018/type-graphql-example-typegoose" 2 | -------------------------------------------------------------------------------- /examples/typegoose/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type User } from "./entities"; 2 | 3 | export interface Context { 4 | user: User; 5 | } 6 | -------------------------------------------------------------------------------- /examples/typegoose/entities/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating"; 2 | export * from "./recipe"; 3 | export * from "./user"; 4 | -------------------------------------------------------------------------------- /examples/typegoose/entities/rating.ts: -------------------------------------------------------------------------------- 1 | import { prop as Property } from "@typegoose/typegoose"; 2 | import { Field, Int, ObjectType } from "type-graphql"; 3 | import { User } from "./user"; 4 | import { Ref } from "../types"; 5 | 6 | @ObjectType() 7 | export class Rating { 8 | @Field(_type => Int) 9 | @Property({ required: true }) 10 | value!: number; 11 | 12 | @Field() 13 | @Property({ default: new Date(), required: true }) 14 | date!: Date; 15 | 16 | @Field(_type => User) 17 | @Property({ ref: User, required: true }) 18 | user!: Ref; 19 | } 20 | -------------------------------------------------------------------------------- /examples/typegoose/entities/recipe.ts: -------------------------------------------------------------------------------- 1 | import { prop as Property, getModelForClass } from "@typegoose/typegoose"; 2 | import { Types } from "mongoose"; 3 | import { Field, ObjectType } from "type-graphql"; 4 | import { Rating } from "./rating"; 5 | import { User } from "./user"; 6 | import { Ref } from "../types"; 7 | 8 | @ObjectType() 9 | export class Recipe { 10 | @Field() 11 | readonly id!: Types.ObjectId; 12 | 13 | @Field() 14 | @Property({ required: true }) 15 | title!: string; 16 | 17 | @Field({ nullable: true }) 18 | @Property() 19 | description?: string; 20 | 21 | @Field(_type => [Rating]) 22 | @Property({ type: () => Rating, default: [] }) 23 | ratings!: Rating[]; 24 | 25 | @Field(_type => User) 26 | @Property({ ref: User, required: true }) 27 | author!: Ref; 28 | } 29 | 30 | export const RecipeModel = getModelForClass(Recipe); 31 | -------------------------------------------------------------------------------- /examples/typegoose/entities/user.ts: -------------------------------------------------------------------------------- 1 | import { prop as Property, getModelForClass } from "@typegoose/typegoose"; 2 | import { Types } from "mongoose"; 3 | import { Field, ObjectType } from "type-graphql"; 4 | 5 | @ObjectType() 6 | export class User { 7 | @Field() 8 | readonly id!: Types.ObjectId; 9 | 10 | @Field() 11 | @Property({ required: true }) 12 | email!: string; 13 | 14 | @Field({ nullable: true }) 15 | @Property() 16 | nickname?: string; 17 | 18 | @Property({ required: true }) 19 | password!: string; 20 | } 21 | 22 | export const UserModel = getModelForClass(User); 23 | -------------------------------------------------------------------------------- /examples/typegoose/object-id.scalar.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLScalarType, Kind } from "graphql"; 2 | import { Types } from "mongoose"; 3 | 4 | export const ObjectIdScalar = new GraphQLScalarType({ 5 | name: "ObjectId", 6 | description: "Mongo object id scalar type", 7 | serialize(value: unknown): string { 8 | if (!(value instanceof Types.ObjectId)) { 9 | throw new Error("ObjectIdScalar can only serialize ObjectId values"); 10 | } 11 | return value.toHexString(); 12 | }, 13 | parseValue(value: unknown): Types.ObjectId { 14 | if (typeof value !== "string") { 15 | throw new Error("ObjectIdScalar can only parse string values"); 16 | } 17 | return new Types.ObjectId(value); 18 | }, 19 | parseLiteral(ast): Types.ObjectId { 20 | if (ast.kind !== Kind.STRING) { 21 | throw new Error("ObjectIdScalar can only parse string values"); 22 | } 23 | return new Types.ObjectId(ast.value); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /examples/typegoose/resolvers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.resolver"; 2 | export * from "./recipe.resolver"; 3 | -------------------------------------------------------------------------------- /examples/typegoose/resolvers/rating.resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { Rating, type User, UserModel } from "../entities"; 3 | 4 | @Resolver(_of => Rating) 5 | export class RatingResolver { 6 | @FieldResolver() 7 | async user(@Root() rating: Rating): Promise { 8 | return (await UserModel.findById(rating.user))!; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/typegoose/resolvers/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.input"; 2 | export * from "./recipe.input"; 3 | -------------------------------------------------------------------------------- /examples/typegoose/resolvers/types/rating.input.ts: -------------------------------------------------------------------------------- 1 | import { Types } from "mongoose"; 2 | import { Field, InputType, Int } from "type-graphql"; 3 | 4 | @InputType() 5 | export class RatingInput { 6 | @Field() 7 | recipeId!: Types.ObjectId; 8 | 9 | @Field(_type => Int) 10 | value!: number; 11 | } 12 | -------------------------------------------------------------------------------- /examples/typegoose/resolvers/types/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "../../entities"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | } 12 | -------------------------------------------------------------------------------- /examples/typegoose/typegoose.middleware.ts: -------------------------------------------------------------------------------- 1 | import { getClass } from "@typegoose/typegoose"; 2 | import { type Document, Model } from "mongoose"; 3 | import { type MiddlewareFn } from "type-graphql"; 4 | 5 | function convertDocument(doc: Document) { 6 | const convertedDocument = doc.toObject(); 7 | const DocumentClass = getClass(doc)!; 8 | Object.setPrototypeOf(convertedDocument, DocumentClass.prototype); 9 | return convertedDocument; 10 | } 11 | 12 | export const TypegooseMiddleware: MiddlewareFn = async (_, next) => { 13 | const result = await next(); 14 | 15 | if (Array.isArray(result)) { 16 | return result.map(item => (item instanceof Model ? convertDocument(item) : item)); 17 | } 18 | 19 | if (result instanceof Model) { 20 | return convertDocument(result); 21 | } 22 | 23 | return result; 24 | }; 25 | -------------------------------------------------------------------------------- /examples/typegoose/types.ts: -------------------------------------------------------------------------------- 1 | import { type Types } from "mongoose"; 2 | 3 | export type Ref = T | Types.ObjectId; 4 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="postgresql://username:password@localhost:5432/type-graphql-example-typeorm-basic" 2 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type User } from "./entities"; 2 | 3 | export interface Context { 4 | user: User; 5 | } 6 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/datasource.ts: -------------------------------------------------------------------------------- 1 | import * as TypeORM from "typeorm"; 2 | import { Rating, Recipe, User } from "./entities"; 3 | 4 | // Create TypeORM dataSource 5 | export const dataSource = new TypeORM.DataSource({ 6 | type: "postgres", 7 | url: process.env.DATABASE_URL, 8 | synchronize: true, 9 | dropSchema: true, 10 | cache: true, 11 | logging: "all", 12 | entities: [Rating, Recipe, User], 13 | logger: "advanced-console", 14 | }); 15 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/entities/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating"; 2 | export * from "./recipe"; 3 | export * from "./user"; 4 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/entities/rating.ts: -------------------------------------------------------------------------------- 1 | import { Field, Int, ObjectType } from "type-graphql"; 2 | import { 3 | Column, 4 | CreateDateColumn, 5 | Entity, 6 | ManyToOne, 7 | PrimaryGeneratedColumn, 8 | RelationId, 9 | } from "typeorm"; 10 | import { Recipe } from "./recipe"; 11 | import { User } from "./user"; 12 | 13 | @Entity() 14 | @ObjectType() 15 | export class Rating { 16 | @PrimaryGeneratedColumn() 17 | readonly id!: number; 18 | 19 | @Field(_type => Int) 20 | @Column({ type: "int" }) 21 | value!: number; 22 | 23 | @Field(_type => User) 24 | @ManyToOne(_type => User) 25 | user!: User; 26 | 27 | @RelationId((rating: Rating) => rating.user) 28 | userId!: number; 29 | 30 | @Field() 31 | @CreateDateColumn() 32 | date!: Date; 33 | 34 | @ManyToOne(_type => Recipe) 35 | recipe!: Recipe; 36 | 37 | @RelationId((rating: Rating) => rating.recipe) 38 | recipeId!: number; 39 | } 40 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/entities/recipe.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, RelationId } from "typeorm"; 3 | import { Rating } from "./rating"; 4 | import { User } from "./user"; 5 | 6 | @Entity() 7 | @ObjectType() 8 | export class Recipe { 9 | @Field(_type => ID) 10 | @PrimaryGeneratedColumn() 11 | readonly id!: number; 12 | 13 | @Field() 14 | @Column() 15 | title!: string; 16 | 17 | @Field({ nullable: true }) 18 | @Column({ nullable: true }) 19 | description?: string; 20 | 21 | @Field(_type => [Rating]) 22 | @OneToMany(_type => Rating, rating => rating.recipe, { cascade: ["insert"] }) 23 | ratings!: Rating[]; 24 | 25 | @Field(_type => User) 26 | @ManyToOne(_type => User) 27 | author!: User; 28 | 29 | @RelationId((recipe: Recipe) => recipe.author) 30 | authorId!: number; 31 | } 32 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/entities/user.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; 3 | 4 | @ObjectType() 5 | @Entity() 6 | export class User { 7 | @Field(_type => ID) 8 | @PrimaryGeneratedColumn() 9 | readonly id!: number; 10 | 11 | @Field() 12 | @Column() 13 | email!: string; 14 | 15 | @Field({ nullable: true }) 16 | @Column({ nullable: true }) 17 | nickname?: string; 18 | 19 | @Column() 20 | password!: string; 21 | } 22 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes { 3 | id 4 | title 5 | author { 6 | email 7 | } 8 | ratings { 9 | value 10 | } 11 | } 12 | } 13 | 14 | query GetRecipe { 15 | recipe(recipeId: 1) { 16 | id 17 | title 18 | ratings { 19 | value 20 | user { 21 | nickname 22 | } 23 | date 24 | } 25 | author { 26 | id 27 | nickname 28 | email 29 | } 30 | } 31 | } 32 | 33 | mutation AddRecipe { 34 | addRecipe(recipe: { title: "New Recipe" }) { 35 | id 36 | ratings { 37 | value 38 | } 39 | author { 40 | nickname 41 | } 42 | } 43 | } 44 | 45 | mutation RatingRecipe { 46 | rating(rating: { recipeId: 3, value: 4 }) { 47 | id 48 | ratings { 49 | value 50 | user { 51 | email 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/resolvers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.resolver"; 2 | export * from "./recipe.resolver"; 3 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/resolvers/rating.resolver.ts: -------------------------------------------------------------------------------- 1 | import { FieldResolver, Resolver, Root } from "type-graphql"; 2 | import { type Repository } from "typeorm"; 3 | import { dataSource } from "../datasource"; 4 | import { Rating, User } from "../entities"; 5 | 6 | @Resolver(_of => Rating) 7 | export class RatingResolver { 8 | private readonly userRepository: Repository; 9 | 10 | constructor() { 11 | this.userRepository = dataSource.getRepository(User); 12 | } 13 | 14 | @FieldResolver() 15 | async user(@Root() rating: Rating): Promise { 16 | return (await this.userRepository.findOne({ where: { id: rating.userId }, cache: 1000 }))!; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/resolvers/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.input"; 2 | export * from "./recipe.input"; 3 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/resolvers/types/rating.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType, Int } from "type-graphql"; 2 | 3 | @InputType() 4 | export class RatingInput { 5 | @Field(_type => Int) 6 | recipeId!: number; 7 | 8 | @Field(_type => Int) 9 | value!: number; 10 | } 11 | -------------------------------------------------------------------------------- /examples/typeorm-basic-usage/resolvers/types/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "../../entities"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | } 12 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="postgresql://username:password@localhost:5432/type-graphql-example-typeorm-lazy" 2 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type User } from "./entities"; 2 | 3 | export interface Context { 4 | user: User; 5 | } 6 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/datasource.ts: -------------------------------------------------------------------------------- 1 | import * as TypeORM from "typeorm"; 2 | import { Rating, Recipe, User } from "./entities"; 3 | 4 | // Create TypeORM dataSource 5 | export const dataSource = new TypeORM.DataSource({ 6 | type: "postgres", 7 | url: process.env.DATABASE_URL, 8 | synchronize: true, 9 | dropSchema: true, 10 | cache: true, 11 | logging: "all", 12 | entities: [Rating, Recipe, User], 13 | logger: "advanced-console", 14 | }); 15 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/entities/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating"; 2 | export * from "./recipe"; 3 | export * from "./user"; 4 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/entities/rating.ts: -------------------------------------------------------------------------------- 1 | import { Field, Int, ObjectType } from "type-graphql"; 2 | import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; 3 | import { Recipe } from "./recipe"; 4 | import { User } from "./user"; 5 | 6 | @Entity() 7 | @ObjectType() 8 | export class Rating { 9 | @PrimaryGeneratedColumn() 10 | readonly id!: number; 11 | 12 | @Field(_type => Int) 13 | @Column({ type: "int" }) 14 | value!: number; 15 | 16 | @Field(_type => User) 17 | @ManyToOne(_type => User, { lazy: true }) 18 | user!: User | Promise; 19 | 20 | @Field() 21 | @CreateDateColumn() 22 | date!: Date; 23 | 24 | @ManyToOne(_type => Recipe, { lazy: true }) 25 | recipe!: Recipe | Promise; 26 | } 27 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/entities/recipe.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; 3 | import { Rating } from "./rating"; 4 | import { User } from "./user"; 5 | 6 | @Entity() 7 | @ObjectType() 8 | export class Recipe { 9 | @Field(_type => ID) 10 | @PrimaryGeneratedColumn() 11 | readonly id!: number; 12 | 13 | @Field() 14 | @Column() 15 | title!: string; 16 | 17 | @Field({ nullable: true }) 18 | @Column({ nullable: true }) 19 | description?: string; 20 | 21 | @Field(_type => [Rating]) 22 | @OneToMany(_type => Rating, rating => rating.recipe, { lazy: true, cascade: ["insert"] }) 23 | ratings!: Rating[] | Promise; 24 | 25 | @Field(_type => User) 26 | @ManyToOne(_type => User, { lazy: true }) 27 | author!: User | Promise; 28 | } 29 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/entities/user.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; 3 | import { Recipe } from "./recipe"; 4 | 5 | @ObjectType() 6 | @Entity() 7 | export class User { 8 | @Field(_type => ID) 9 | @PrimaryGeneratedColumn() 10 | readonly id!: number; 11 | 12 | @Field() 13 | @Column() 14 | email!: string; 15 | 16 | @Field({ nullable: true }) 17 | @Column({ nullable: true }) 18 | nickname?: string; 19 | 20 | @Column() 21 | password!: string; 22 | 23 | @OneToMany(_type => Recipe, recipe => recipe.author, { lazy: true }) 24 | @Field(_type => [Recipe]) 25 | recipes!: Recipe[] | Promise; 26 | } 27 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipes { 2 | recipes { 3 | id 4 | title 5 | author { 6 | id 7 | email 8 | nickname 9 | } 10 | ratings { 11 | value 12 | } 13 | } 14 | } 15 | 16 | query GetRecipe { 17 | recipe(recipeId: 1) { 18 | id 19 | title 20 | ratings { 21 | value 22 | user { 23 | nickname 24 | } 25 | date 26 | } 27 | author { 28 | nickname 29 | recipes { 30 | title 31 | } 32 | } 33 | } 34 | } 35 | 36 | mutation AddRecipe { 37 | addRecipe(recipe: { title: "New Recipe" }) { 38 | id 39 | ratings { 40 | value 41 | } 42 | author { 43 | nickname 44 | } 45 | } 46 | } 47 | 48 | mutation RatingRecipe { 49 | rating(rating: { recipeId: 3, value: 4 }) { 50 | id 51 | ratings { 52 | value 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/resolvers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./recipe.resolver"; 2 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/resolvers/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./rating.input"; 2 | export * from "./recipe.input"; 3 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/resolvers/types/rating.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType, Int } from "type-graphql"; 2 | 3 | @InputType() 4 | export class RatingInput { 5 | @Field(_type => Int) 6 | recipeId!: number; 7 | 8 | @Field(_type => Int) 9 | value!: number; 10 | } 11 | -------------------------------------------------------------------------------- /examples/typeorm-lazy-relations/resolvers/types/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "../../entities"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description!: string; 11 | } 12 | -------------------------------------------------------------------------------- /examples/using-container/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipe1 { 2 | recipe(recipeId: "1") { 3 | title 4 | description 5 | ingredients 6 | numberInCollection 7 | } 8 | } 9 | 10 | query GetRecipes { 11 | recipes { 12 | title 13 | description 14 | ingredientsLength 15 | numberInCollection 16 | } 17 | } 18 | 19 | mutation AddRecipe { 20 | addRecipe(recipe: { title: "New recipe", ingredients: ["One", "Two", "Three"] }) { 21 | id 22 | numberInCollection 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/using-container/recipe.data.ts: -------------------------------------------------------------------------------- 1 | import { Recipe } from "./recipe.type"; 2 | 3 | function createRecipe(recipeData: Partial): Recipe { 4 | return Object.assign(new Recipe(), recipeData); 5 | } 6 | 7 | export const sampleRecipes = [ 8 | createRecipe({ 9 | id: "1", 10 | title: "Recipe 1", 11 | description: "Desc 1", 12 | ingredients: ["one", "two", "three"], 13 | }), 14 | createRecipe({ 15 | id: "2", 16 | title: "Recipe 2", 17 | description: "Desc 2", 18 | ingredients: ["four", "five", "six"], 19 | }), 20 | createRecipe({ 21 | id: "3", 22 | title: "Recipe 3", 23 | ingredients: ["seven", "eight", "nine"], 24 | }), 25 | ]; 26 | -------------------------------------------------------------------------------- /examples/using-container/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "./recipe.type"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | 12 | @Field(_type => [String]) 13 | ingredients!: string[]; 14 | } 15 | -------------------------------------------------------------------------------- /examples/using-container/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, Int, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field(_type => ID) 6 | id!: string; 7 | 8 | @Field() 9 | title!: string; 10 | 11 | @Field({ nullable: true }) 12 | description?: string; 13 | 14 | @Field(_type => [String]) 15 | ingredients!: string[]; 16 | 17 | @Field(_type => Int) 18 | protected numberInCollection!: number; 19 | 20 | @Field(_type => Int) 21 | protected get ingredientsLength(): number { 22 | return this.ingredients.length; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/using-container/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | addRecipe(recipe: RecipeInput!): Recipe! 8 | } 9 | 10 | type Query { 11 | recipe(recipeId: String!): Recipe 12 | recipes: [Recipe!]! 13 | } 14 | 15 | type Recipe { 16 | description: String 17 | id: ID! 18 | ingredients: [String!]! 19 | ingredientsLength: Int! 20 | numberInCollection: Int! 21 | title: String! 22 | } 23 | 24 | input RecipeInput { 25 | description: String 26 | ingredients: [String!]! 27 | title: String! 28 | } 29 | -------------------------------------------------------------------------------- /examples/using-scoped-container/context.type.ts: -------------------------------------------------------------------------------- 1 | import { type ContainerInstance } from "typedi"; 2 | 3 | export interface Context { 4 | requestId: number; 5 | container: ContainerInstance; 6 | } 7 | -------------------------------------------------------------------------------- /examples/using-scoped-container/examples.graphql: -------------------------------------------------------------------------------- 1 | query GetRecipe1 { 2 | recipe(recipeId: "1") { 3 | title 4 | description 5 | } 6 | } 7 | 8 | query GetNotExistingRecipe10 { 9 | recipe(recipeId: "10") { 10 | title 11 | description 12 | } 13 | } 14 | 15 | query GetRecipes { 16 | recipes { 17 | title 18 | description 19 | } 20 | } 21 | 22 | mutation AddRecipe { 23 | addRecipe(recipe: { title: "New recipe", ingredients: ["One", "Two", "Three"] }) { 24 | id 25 | title 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/using-scoped-container/logger.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Service } from "typedi"; 2 | import { Context } from "./context.type"; 3 | 4 | // Service is recreated for each request (scoped) 5 | @Service() 6 | export class Logger { 7 | constructor(@Inject("context") private readonly context: Context) { 8 | console.log("Logger created!"); 9 | } 10 | 11 | log(...messages: any[]) { 12 | console.log(`(ID ${this.context.requestId}):`, ...messages); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/using-scoped-container/recipe/recipe.input.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "type-graphql"; 2 | import { type Recipe } from "./recipe.type"; 3 | 4 | @InputType() 5 | export class RecipeInput implements Partial { 6 | @Field() 7 | title!: string; 8 | 9 | @Field({ nullable: true }) 10 | description!: string; 11 | 12 | @Field(_type => [String]) 13 | ingredients!: string[]; 14 | } 15 | -------------------------------------------------------------------------------- /examples/using-scoped-container/recipe/recipe.type.ts: -------------------------------------------------------------------------------- 1 | import { Field, ID, ObjectType } from "type-graphql"; 2 | 3 | @ObjectType() 4 | export class Recipe { 5 | @Field(_type => ID) 6 | id!: string; 7 | 8 | @Field() 9 | title!: string; 10 | 11 | @Field({ nullable: true }) 12 | description?: string; 13 | 14 | @Field(_type => [String]) 15 | ingredients!: string[]; 16 | } 17 | -------------------------------------------------------------------------------- /examples/using-scoped-container/schema.graphql: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! 3 | # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! 4 | # ----------------------------------------------- 5 | 6 | type Mutation { 7 | addRecipe(recipe: RecipeInput!): Recipe! 8 | } 9 | 10 | type Query { 11 | recipe(recipeId: String!): Recipe 12 | recipes: [Recipe!]! 13 | } 14 | 15 | type Recipe { 16 | description: String 17 | id: ID! 18 | ingredients: [String!]! 19 | title: String! 20 | } 21 | 22 | input RecipeInput { 23 | description: String 24 | ingredients: [String!]! 25 | title: String! 26 | } 27 | -------------------------------------------------------------------------------- /images/admin-remix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/admin-remix.png -------------------------------------------------------------------------------- /images/alka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/alka.png -------------------------------------------------------------------------------- /images/blue_receipt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/blue_receipt.gif -------------------------------------------------------------------------------- /images/c19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/c19.png -------------------------------------------------------------------------------- /images/ecad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/ecad.png -------------------------------------------------------------------------------- /images/ecad_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/ecad_new.png -------------------------------------------------------------------------------- /images/famety.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/famety.png -------------------------------------------------------------------------------- /images/fansoria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/fansoria.png -------------------------------------------------------------------------------- /images/flatirons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/flatirons.png -------------------------------------------------------------------------------- /images/gorrion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/gorrion.png -------------------------------------------------------------------------------- /images/ig-comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/ig-comment.png -------------------------------------------------------------------------------- /images/insfollowpro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/insfollowpro.png -------------------------------------------------------------------------------- /images/intexsoft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/intexsoft.jpg -------------------------------------------------------------------------------- /images/leofame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/leofame.png -------------------------------------------------------------------------------- /images/lifex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/lifex.png -------------------------------------------------------------------------------- /images/live-graphics-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/live-graphics-system.png -------------------------------------------------------------------------------- /images/logo-buzzv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/logo-buzzv.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/logo.png -------------------------------------------------------------------------------- /images/lovd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/lovd.png -------------------------------------------------------------------------------- /images/mr-yum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/mr-yum.png -------------------------------------------------------------------------------- /images/non-stop-casino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/non-stop-casino.png -------------------------------------------------------------------------------- /images/nongamstopbets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/nongamstopbets.png -------------------------------------------------------------------------------- /images/play_fortune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/play_fortune.png -------------------------------------------------------------------------------- /images/polis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/polis.png -------------------------------------------------------------------------------- /images/pwmlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/pwmlogo.png -------------------------------------------------------------------------------- /images/qulix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/qulix.png -------------------------------------------------------------------------------- /images/sidesmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/sidesmedia.png -------------------------------------------------------------------------------- /images/slon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/slon.png -------------------------------------------------------------------------------- /images/social_followers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/social_followers.png -------------------------------------------------------------------------------- /images/socialmention.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/socialmention.webp -------------------------------------------------------------------------------- /images/socialwick-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/socialwick-logo.png -------------------------------------------------------------------------------- /images/swiss-mentor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/swiss-mentor.png -------------------------------------------------------------------------------- /images/tiktok-expert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/tiktok-expert.jpg -------------------------------------------------------------------------------- /images/use-viral-logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/use-viral-logo.webp -------------------------------------------------------------------------------- /images/vps-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/vps-server.png -------------------------------------------------------------------------------- /images/wordhint.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/images/wordhint.jpg -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { pathsToModuleNameMapper, JestConfigWithTsJest } from "ts-jest"; 2 | import tsconfig from "./tsconfig.json"; 3 | 4 | export default { 5 | preset: "ts-jest", 6 | verbose: false, 7 | rootDir: "./", 8 | roots: ["/src", "/tests"], 9 | testEnvironment: "node", 10 | collectCoverage: false, 11 | collectCoverageFrom: [ 12 | "/src/**/*.ts", 13 | "!/src/**/*.d.ts", 14 | "!/src/shim.ts", 15 | ], 16 | moduleNameMapper: pathsToModuleNameMapper(tsconfig.compilerOptions.paths, { 17 | prefix: "", 18 | }), 19 | transform: { 20 | "^.+\\.tsx?$": ["ts-jest", { tsconfig: "./tests/tsconfig.json" }], 21 | }, 22 | testMatch: ["**/functional/**/*.ts"], 23 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 24 | coverageDirectory: "/coverage", 25 | } satisfies JestConfigWithTsJest; 26 | -------------------------------------------------------------------------------- /scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "import/no-extraneous-dependencies": [ 4 | "error", 5 | { 6 | "devDependencies": true 7 | } 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true 5 | }, 6 | "include": [".", "../src"] 7 | } 8 | -------------------------------------------------------------------------------- /scripts/version.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ts-node 2 | 3 | import packageJson from "../package.json"; 4 | import { graphQLPeerDependencyVersion } from "../src/utils/graphql-version"; 5 | 6 | if (graphQLPeerDependencyVersion !== packageJson.peerDependencies.graphql) { 7 | throw new Error( 8 | `GraphQL peer dependency version (${graphQLPeerDependencyVersion}) != package.json.peerDependencies.graphql (${packageJson.peerDependencies.graphql})`, 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/decorators/ArgsType.ts: -------------------------------------------------------------------------------- 1 | import { getMetadataStorage } from "@/metadata/getMetadataStorage"; 2 | 3 | export function ArgsType(): ClassDecorator { 4 | return target => { 5 | getMetadataStorage().collectArgsMetadata({ 6 | name: target.name, 7 | target, 8 | }); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/decorators/Ctx.ts: -------------------------------------------------------------------------------- 1 | import { SymbolKeysNotSupportedError } from "@/errors"; 2 | import { getMetadataStorage } from "@/metadata/getMetadataStorage"; 3 | import { type ParameterDecorator } from "@/typings"; 4 | 5 | export function Ctx(propertyName?: string): ParameterDecorator { 6 | return (prototype, propertyKey, parameterIndex) => { 7 | if (typeof propertyKey === "symbol") { 8 | throw new SymbolKeysNotSupportedError(); 9 | } 10 | 11 | getMetadataStorage().collectHandlerParamMetadata({ 12 | kind: "context", 13 | target: prototype.constructor, 14 | methodName: propertyKey, 15 | index: parameterIndex, 16 | propertyName, 17 | }); 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/decorators/Info.ts: -------------------------------------------------------------------------------- 1 | import { SymbolKeysNotSupportedError } from "@/errors"; 2 | import { getMetadataStorage } from "@/metadata/getMetadataStorage"; 3 | import { type ParameterDecorator } from "@/typings"; 4 | 5 | export function Info(): ParameterDecorator { 6 | return (prototype, propertyKey, parameterIndex) => { 7 | if (typeof propertyKey === "symbol") { 8 | throw new SymbolKeysNotSupportedError(); 9 | } 10 | 11 | getMetadataStorage().collectHandlerParamMetadata({ 12 | kind: "info", 13 | target: prototype.constructor, 14 | methodName: propertyKey, 15 | index: parameterIndex, 16 | }); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/decorators/InputType.ts: -------------------------------------------------------------------------------- 1 | import { getNameDecoratorParams } from "@/helpers/decorators"; 2 | import { getMetadataStorage } from "@/metadata/getMetadataStorage"; 3 | import { type DescriptionOptions } from "./types"; 4 | 5 | export type InputTypeOptions = DescriptionOptions; 6 | 7 | export function InputType(): ClassDecorator; 8 | export function InputType(options: InputTypeOptions): ClassDecorator; 9 | export function InputType(name: string, options?: InputTypeOptions): ClassDecorator; 10 | export function InputType( 11 | nameOrOptions?: string | InputTypeOptions, 12 | maybeOptions?: InputTypeOptions, 13 | ): ClassDecorator { 14 | const { name, options } = getNameDecoratorParams(nameOrOptions, maybeOptions); 15 | return target => { 16 | getMetadataStorage().collectInputMetadata({ 17 | name: name || target.name, 18 | target, 19 | description: options.description, 20 | }); 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/decorators/createMethodMiddlewareDecorator.ts: -------------------------------------------------------------------------------- 1 | import { type MiddlewareFn } from "@/typings/middleware"; 2 | import { UseMiddleware } from "./UseMiddleware"; 3 | 4 | export function createMethodMiddlewareDecorator( 5 | resolver: MiddlewareFn, 6 | ): MethodDecorator { 7 | return UseMiddleware(resolver); 8 | } 9 | -------------------------------------------------------------------------------- /src/decorators/createResolverClassMiddlewareDecorator.ts: -------------------------------------------------------------------------------- 1 | import { type MiddlewareFn } from "@/typings"; 2 | import { UseMiddleware } from "./UseMiddleware"; 3 | 4 | export function createResolverClassMiddlewareDecorator( 5 | resolver: MiddlewareFn, 6 | ): ClassDecorator { 7 | return UseMiddleware(resolver); 8 | } 9 | -------------------------------------------------------------------------------- /src/decorators/enums.ts: -------------------------------------------------------------------------------- 1 | import { getMetadataStorage } from "@/metadata/getMetadataStorage"; 2 | import { type EnumConfig } from "./types"; 3 | 4 | export function registerEnumType( 5 | enumObj: TEnum, 6 | enumConfig: EnumConfig, 7 | ) { 8 | getMetadataStorage().collectEnumMetadata({ 9 | enumObj, 10 | name: enumConfig.name, 11 | description: enumConfig.description, 12 | valuesConfig: enumConfig.valuesConfig || {}, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/errors/CannotDetermineGraphQLTypeError.ts: -------------------------------------------------------------------------------- 1 | export class CannotDetermineGraphQLTypeError extends Error { 2 | constructor( 3 | typeKind: "input" | "output", 4 | typeName: string, 5 | propertyKey: string, 6 | parameterIndex?: number, 7 | argName?: string, 8 | ) { 9 | let errorMessage = `Cannot determine GraphQL ${typeKind} type for `; 10 | if (argName) { 11 | errorMessage += `argument named '${argName}' of `; 12 | } else if (parameterIndex !== undefined) { 13 | errorMessage += `parameter #${parameterIndex} of `; 14 | } 15 | errorMessage += 16 | `'${propertyKey}' of '${typeName}' class. ` + 17 | `Is the value, that is used as its TS type or explicit type, decorated with a proper ` + 18 | `decorator or is it a proper ${typeKind} value?`; 19 | 20 | super(errorMessage); 21 | Object.setPrototypeOf(this, new.target.prototype); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/errors/ConflictingDefaultValuesError.ts: -------------------------------------------------------------------------------- 1 | export class ConflictingDefaultValuesError extends Error { 2 | constructor( 3 | typeName: string, 4 | fieldName: string, 5 | defaultValueFromDecorator: unknown, 6 | defaultValueFromInitializer: unknown, 7 | ) { 8 | super( 9 | `The '${fieldName}' field of '${typeName}' has conflicting default values. ` + 10 | `Default value from decorator ('${defaultValueFromDecorator}') ` + 11 | `is not equal to the property initializer value ('${defaultValueFromInitializer}').`, 12 | ); 13 | 14 | Object.setPrototypeOf(this, new.target.prototype); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/errors/GeneratingSchemaError.ts: -------------------------------------------------------------------------------- 1 | import { type GraphQLError } from "graphql"; 2 | 3 | export class GeneratingSchemaError extends Error { 4 | details: readonly GraphQLError[]; 5 | 6 | constructor(details: readonly GraphQLError[]) { 7 | let errorMessage = "Some errors occurred while generating GraphQL schema:\n"; 8 | errorMessage += details.map(it => ` ${it.message}\n`); 9 | errorMessage += "Please check the `details` property of the error to get more detailed info."; 10 | 11 | super(errorMessage); 12 | Object.setPrototypeOf(this, new.target.prototype); 13 | 14 | this.details = details; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/errors/InterfaceResolveTypeError.ts: -------------------------------------------------------------------------------- 1 | import { type ClassMetadata } from "@/metadata/definitions"; 2 | 3 | export class InterfaceResolveTypeError extends Error { 4 | constructor(interfaceMetadata: ClassMetadata) { 5 | super( 6 | `Cannot resolve type for interface ${interfaceMetadata.name}! ` + 7 | `You need to return instance of object type class, not a plain object!`, 8 | ); 9 | 10 | Object.setPrototypeOf(this, new.target.prototype); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/errors/InvalidDirectiveError.ts: -------------------------------------------------------------------------------- 1 | export class InvalidDirectiveError extends Error { 2 | constructor(msg: string) { 3 | super(msg); 4 | Object.setPrototypeOf(this, new.target.prototype); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/errors/MissingPubSubError.ts: -------------------------------------------------------------------------------- 1 | export class MissingPubSubError extends Error { 2 | constructor() { 3 | super( 4 | "Looks like you've forgot to provide `pubSub` option in `buildSchema()`. " + 5 | "Subscriptions can't work without a proper PubSub system.", 6 | ); 7 | 8 | Object.setPrototypeOf(this, new.target.prototype); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/errors/MissingSubscriptionTopicsError.ts: -------------------------------------------------------------------------------- 1 | export class MissingSubscriptionTopicsError extends Error { 2 | constructor(target: Function, methodName: string) { 3 | super(`${target.name}#${methodName} subscription has no provided topics!`); 4 | 5 | Object.setPrototypeOf(this, new.target.prototype); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/errors/NoExplicitTypeError.ts: -------------------------------------------------------------------------------- 1 | export class NoExplicitTypeError extends Error { 2 | constructor(typeName: string, propertyKey: string, parameterIndex?: number, argName?: string) { 3 | let errorMessage = 4 | `Unable to infer GraphQL type from TypeScript reflection system. ` + 5 | `You need to provide explicit type for `; 6 | if (argName) { 7 | errorMessage += `argument named '${argName}' of `; 8 | } else if (parameterIndex !== undefined) { 9 | errorMessage += `parameter #${parameterIndex} of `; 10 | } 11 | errorMessage += `'${propertyKey}' of '${typeName}' class.`; 12 | super(errorMessage); 13 | 14 | Object.setPrototypeOf(this, new.target.prototype); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/errors/ReflectMetadataMissingError.ts: -------------------------------------------------------------------------------- 1 | export class ReflectMetadataMissingError extends Error { 2 | constructor() { 3 | super( 4 | "Looks like you've forgot to provide experimental metadata API polyfill. " + 5 | "Please read the installation instruction for more details.", 6 | ); 7 | 8 | Object.setPrototypeOf(this, new.target.prototype); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/errors/SymbolKeysNotSupportedError.ts: -------------------------------------------------------------------------------- 1 | export class SymbolKeysNotSupportedError extends Error { 2 | constructor() { 3 | super("Symbol keys are not supported yet!"); 4 | 5 | Object.setPrototypeOf(this, new.target.prototype); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/errors/UnionResolveTypeError.ts: -------------------------------------------------------------------------------- 1 | import { type UnionMetadata } from "@/metadata/definitions"; 2 | 3 | export class UnionResolveTypeError extends Error { 4 | constructor(unionMetadata: UnionMetadata) { 5 | super( 6 | `Cannot resolve type for union ${unionMetadata.name}! ` + 7 | `You need to return instance of object type class, not a plain object!`, 8 | ); 9 | 10 | Object.setPrototypeOf(this, new.target.prototype); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/errors/UnmetGraphQLPeerDependencyError.ts: -------------------------------------------------------------------------------- 1 | export class UnmetGraphQLPeerDependencyError extends Error { 2 | constructor(graphQLVersion: string, graphQLPeerDependencyVersion: string) { 3 | super( 4 | `Looks like you use an incorrect version of the 'graphql' package: "${graphQLVersion}". ` + 5 | `Please ensure that you have installed a version ` + 6 | `that meets TypeGraphQL's requirement: "${graphQLPeerDependencyVersion}".`, 7 | ); 8 | 9 | Object.setPrototypeOf(this, new.target.prototype); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/errors/WrongNullableListOptionError.ts: -------------------------------------------------------------------------------- 1 | import { type NullableListOptions } from "@/decorators/types"; 2 | 3 | export class WrongNullableListOptionError extends Error { 4 | constructor( 5 | targetName: string, 6 | propertyName: string, 7 | nullable: boolean | NullableListOptions | undefined, 8 | ) { 9 | super( 10 | `Wrong nullable option set for ${targetName}#${propertyName}. ` + 11 | `You cannot combine non-list type with nullable '${nullable}'.`, 12 | ); 13 | 14 | Object.setPrototypeOf(this, new.target.prototype); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/errors/graphql/ArgumentValidationError.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 2 | // @ts-ignore `class-validator` might not be installed by user 3 | import { type ValidationError } from "class-validator"; 4 | import { GraphQLError } from "graphql"; 5 | 6 | export class ArgumentValidationError extends GraphQLError { 7 | override readonly extensions!: { 8 | code: "BAD_USER_INPUT"; 9 | validationErrors: ValidationError[]; 10 | [attributeName: string]: unknown; // GraphQLErrorExtensions 11 | }; 12 | 13 | constructor(validationErrors: ValidationError[]) { 14 | super("Argument Validation Error", { 15 | extensions: { 16 | code: "BAD_USER_INPUT", 17 | validationErrors, 18 | }, 19 | }); 20 | 21 | Object.setPrototypeOf(this, new.target.prototype); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/errors/graphql/AuthenticationError.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLError } from "graphql"; 2 | 3 | export class AuthenticationError extends GraphQLError { 4 | override readonly extensions!: { 5 | code: "UNAUTHENTICATED"; 6 | [attributeName: string]: unknown; // GraphQLErrorExtensions 7 | }; 8 | 9 | constructor(message = "Access denied! You need to be authenticated to perform this action!") { 10 | super(message, { 11 | extensions: { 12 | code: "UNAUTHENTICATED", 13 | }, 14 | }); 15 | 16 | Object.setPrototypeOf(this, new.target.prototype); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/errors/graphql/AuthorizationError.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLError } from "graphql"; 2 | 3 | export class AuthorizationError extends GraphQLError { 4 | override readonly extensions!: { 5 | code: "UNAUTHORIZED"; 6 | [attributeName: string]: unknown; // GraphQLErrorExtensions 7 | }; 8 | 9 | constructor(message = "Access denied! You don't have permission for this action!") { 10 | super(message, { 11 | extensions: { 12 | code: "UNAUTHORIZED", 13 | }, 14 | }); 15 | 16 | Object.setPrototypeOf(this, new.target.prototype); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/errors/graphql/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AuthenticationError"; 2 | export * from "./AuthorizationError"; 3 | export * from "./ArgumentValidationError"; 4 | -------------------------------------------------------------------------------- /src/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./graphql"; 2 | export * from "./CannotDetermineGraphQLTypeError"; 3 | export * from "./GeneratingSchemaError"; 4 | export * from "./ConflictingDefaultValuesError"; 5 | export * from "./InterfaceResolveTypeError"; 6 | export * from "./InvalidDirectiveError"; 7 | export * from "./MissingPubSubError"; 8 | export * from "./MissingSubscriptionTopicsError"; 9 | export * from "./NoExplicitTypeError"; 10 | export * from "./ReflectMetadataMissingError"; 11 | export * from "./SymbolKeysNotSupportedError"; 12 | export * from "./UnionResolveTypeError"; 13 | export * from "./UnmetGraphQLPeerDependencyError"; 14 | export * from "./WrongNullableListOptionError"; 15 | -------------------------------------------------------------------------------- /src/helpers/filesystem.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import asyncFs from "node:fs/promises"; 3 | import path from "node:path"; 4 | 5 | export async function outputFile(filePath: string, fileContent: any) { 6 | try { 7 | await asyncFs.writeFile(filePath, fileContent); 8 | } catch (err) { 9 | if ((err as NodeJS.ErrnoException).code !== "ENOENT") { 10 | throw err; 11 | } 12 | await asyncFs.mkdir(path.dirname(filePath), { recursive: true }); 13 | await asyncFs.writeFile(filePath, fileContent); 14 | } 15 | } 16 | 17 | export function outputFileSync(filePath: string, fileContent: any) { 18 | try { 19 | fs.writeFileSync(filePath, fileContent); 20 | } catch (err) { 21 | if ((err as NodeJS.ErrnoException).code !== "ENOENT") { 22 | throw err; 23 | } 24 | fs.mkdirSync(path.dirname(filePath), { recursive: true }); 25 | fs.writeFileSync(filePath, fileContent); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/helpers/isThrowing.ts: -------------------------------------------------------------------------------- 1 | export function isThrowing(fn: () => void) { 2 | try { 3 | fn(); 4 | return false; 5 | } catch { 6 | return true; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/helpers/returnTypes.ts: -------------------------------------------------------------------------------- 1 | export const allowedTypes: Function[] = [String, Number, Date, Boolean]; 2 | export const bannedTypes: Function[] = [Promise, Array, Object, Function]; 3 | -------------------------------------------------------------------------------- /src/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | export type ArrayElements = 2 | TArray extends ReadonlyArray ? TElement : never; 3 | 4 | export type UnionFromClasses = InstanceType< 5 | ArrayElements 6 | >; 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./decorators"; 2 | export * from "./errors"; 3 | export * from "./typings"; 4 | export * from "./metadata"; 5 | export * from "./scalars"; 6 | export * from "./utils"; 7 | -------------------------------------------------------------------------------- /src/metadata/definitions/authorized-metadata.ts: -------------------------------------------------------------------------------- 1 | export interface AuthorizedMetadata { 2 | target: Function; 3 | fieldName: string; 4 | roles: any[]; 5 | } 6 | 7 | export type AuthorizedClassMetadata = Omit; 8 | -------------------------------------------------------------------------------- /src/metadata/definitions/class-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type DirectiveMetadata } from "./directive-metadata"; 2 | import { type ExtensionsMetadata } from "./extensions-metadata"; 3 | import { type FieldMetadata } from "./field-metadata"; 4 | 5 | export interface ClassMetadata { 6 | name: string; 7 | target: Function; 8 | fields?: FieldMetadata[]; 9 | description?: string; 10 | directives?: DirectiveMetadata[]; 11 | extensions?: ExtensionsMetadata; 12 | simpleResolvers?: boolean; 13 | } 14 | -------------------------------------------------------------------------------- /src/metadata/definitions/directive-metadata.ts: -------------------------------------------------------------------------------- 1 | export interface DirectiveMetadata { 2 | nameOrDefinition: string; 3 | args: Record; 4 | } 5 | 6 | export interface DirectiveClassMetadata { 7 | target: Function; 8 | directive: DirectiveMetadata; 9 | } 10 | 11 | export interface DirectiveFieldMetadata { 12 | target: Function; 13 | fieldName: string; 14 | directive: DirectiveMetadata; 15 | } 16 | 17 | export interface DirectiveArgumentMetadata { 18 | target: Function; 19 | fieldName: string; 20 | parameterIndex: number; 21 | directive: DirectiveMetadata; 22 | } 23 | -------------------------------------------------------------------------------- /src/metadata/definitions/enum-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type EnumValuesConfig } from "@/decorators/types"; 2 | 3 | export interface EnumMetadata { 4 | enumObj: object; 5 | name: string; 6 | description: string | undefined; 7 | valuesConfig: EnumValuesConfig; 8 | } 9 | -------------------------------------------------------------------------------- /src/metadata/definitions/extensions-metadata.ts: -------------------------------------------------------------------------------- 1 | export type ExtensionsMetadata = Readonly>; 2 | 3 | export interface ExtensionsClassMetadata { 4 | target: Function; 5 | extensions: ExtensionsMetadata; 6 | } 7 | 8 | export interface ExtensionsFieldMetadata { 9 | target: Function; 10 | fieldName: string; 11 | extensions: ExtensionsMetadata; 12 | } 13 | -------------------------------------------------------------------------------- /src/metadata/definitions/field-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type TypeOptions, type TypeValueThunk } from "@/decorators/types"; 2 | import { type Complexity } from "@/typings"; 3 | import { type Middleware } from "@/typings/middleware"; 4 | import { type DirectiveMetadata } from "./directive-metadata"; 5 | import { type ExtensionsMetadata } from "./extensions-metadata"; 6 | import { type ParamMetadata } from "./param-metadata"; 7 | 8 | export interface FieldMetadata { 9 | target: Function; 10 | schemaName: string; 11 | name: string; 12 | getType: TypeValueThunk; 13 | typeOptions: TypeOptions; 14 | description: string | undefined; 15 | deprecationReason: string | undefined; 16 | complexity: Complexity | undefined; 17 | params?: ParamMetadata[]; 18 | roles?: any[]; 19 | middlewares?: Array>; 20 | directives?: DirectiveMetadata[]; 21 | extensions?: ExtensionsMetadata; 22 | simple?: boolean; 23 | } 24 | -------------------------------------------------------------------------------- /src/metadata/definitions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./authorized-metadata"; 2 | export * from "./class-metadata"; 3 | export * from "./directive-metadata"; 4 | export * from "./enum-metadata"; 5 | export * from "./extensions-metadata"; 6 | export * from "./field-metadata"; 7 | export * from "./middleware-metadata"; 8 | export * from "./param-metadata"; 9 | export * from "./resolver-metadata"; 10 | export * from "./union-metadata"; 11 | -------------------------------------------------------------------------------- /src/metadata/definitions/interface-class-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type TypeResolver } from "@/typings"; 2 | import { type ClassMetadata } from "./class-metadata"; 3 | 4 | export type InterfaceClassMetadata = { 5 | resolveType?: TypeResolver; 6 | autoRegisteringDisabled: boolean; 7 | interfaceClasses: Function[] | undefined; 8 | } & ClassMetadata; 9 | -------------------------------------------------------------------------------- /src/metadata/definitions/middleware-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type Middleware } from "@/typings/middleware"; 2 | 3 | export interface MiddlewareMetadata { 4 | target: Function; 5 | fieldName: string; 6 | middlewares: Array>; 7 | } 8 | 9 | export type ResolverMiddlewareMetadata = Omit; 10 | -------------------------------------------------------------------------------- /src/metadata/definitions/object-class-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type ClassMetadata } from "./class-metadata"; 2 | 3 | export type ObjectClassMetadata = { 4 | interfaceClasses: Function[] | undefined; 5 | } & ClassMetadata; 6 | -------------------------------------------------------------------------------- /src/metadata/definitions/union-metadata.ts: -------------------------------------------------------------------------------- 1 | import { type ClassType, type TypeResolver } from "@/typings"; 2 | 3 | export interface UnionMetadata { 4 | getClassTypes: () => ClassType[]; 5 | name: string; 6 | description?: string; 7 | resolveType?: TypeResolver; 8 | } 9 | export type UnionMetadataWithSymbol = { 10 | symbol: symbol; 11 | } & UnionMetadata; 12 | -------------------------------------------------------------------------------- /src/metadata/getMetadataStorage.ts: -------------------------------------------------------------------------------- 1 | import { MetadataStorage } from "./metadata-storage"; 2 | 3 | declare global { 4 | // eslint-disable-next-line vars-on-top, no-var 5 | var TypeGraphQLMetadataStorage: MetadataStorage; 6 | } 7 | 8 | export function getMetadataStorage(): MetadataStorage { 9 | if (!global.TypeGraphQLMetadataStorage) { 10 | global.TypeGraphQLMetadataStorage = new MetadataStorage(); 11 | } 12 | 13 | return global.TypeGraphQLMetadataStorage; 14 | } 15 | -------------------------------------------------------------------------------- /src/metadata/index.ts: -------------------------------------------------------------------------------- 1 | export { getMetadataStorage } from "./getMetadataStorage"; 2 | -------------------------------------------------------------------------------- /src/scalars/aliases.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLFloat, GraphQLID, GraphQLInt } from "graphql"; 2 | 3 | export const Int = GraphQLInt; 4 | export const Float = GraphQLFloat; 5 | export const ID = GraphQLID; 6 | -------------------------------------------------------------------------------- /src/scalars/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./aliases"; 2 | export { GraphQLTimestamp, GraphQLDateTimeISO as GraphQLISODateTime } from "graphql-scalars"; 3 | -------------------------------------------------------------------------------- /src/typings/Complexity.ts: -------------------------------------------------------------------------------- 1 | import { type ComplexityEstimator } from "graphql-query-complexity"; 2 | 3 | export type Complexity = ComplexityEstimator | number; 4 | -------------------------------------------------------------------------------- /src/typings/ResolverInterface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Resolver classes can implement this type 3 | * to provide a proper resolver method signatures for fields of T. 4 | */ 5 | export type ResolverInterface = { 6 | [P in keyof T]?: (root: T, ...args: any[]) => T[P] | Promise; 7 | }; 8 | -------------------------------------------------------------------------------- /src/typings/SubscribeResolverData.ts: -------------------------------------------------------------------------------- 1 | import { type GraphQLResolveInfo } from "graphql"; 2 | import { type ArgsDictionary } from "./resolver-data"; 3 | 4 | export interface SubscribeResolverData { 5 | source: TSource; 6 | args: TArgs; 7 | context: TContext; 8 | info: GraphQLResolveInfo; 9 | } 10 | -------------------------------------------------------------------------------- /src/typings/SubscriptionHandlerData.ts: -------------------------------------------------------------------------------- 1 | import { type GraphQLResolveInfo } from "graphql"; 2 | import { type ArgsDictionary } from "./resolver-data"; 3 | 4 | export interface SubscriptionHandlerData { 5 | payload: TPayload; 6 | args: TArgs; 7 | context: TContext; 8 | info: GraphQLResolveInfo; 9 | } 10 | -------------------------------------------------------------------------------- /src/typings/TypeResolver.ts: -------------------------------------------------------------------------------- 1 | import { type GraphQLTypeResolver } from "graphql"; 2 | import { type ClassType, type Maybe, type MaybePromise } from "./utils"; 3 | 4 | export type TypeResolver = ( 5 | ...args: Parameters> 6 | ) => MaybePromise>; 7 | -------------------------------------------------------------------------------- /src/typings/ValidatorFn.ts: -------------------------------------------------------------------------------- 1 | import { type TypeValue } from "@/decorators/types"; 2 | import { type ResolverData } from "./resolver-data"; 3 | 4 | export type ValidatorFn = ( 5 | /** 6 | * The value of the argument. 7 | * It can by of any type, which means: 8 | * - undefined or null (if the argument is nullable) 9 | * - primitive type (string, number, boolean) 10 | * - underlying scalar type (Date, Buffer, etc.) 11 | * - object type (arg type class instance) 12 | * - array type (array of any of the above) 13 | */ 14 | argValue: any | undefined, 15 | argType: TypeValue, 16 | resolverData: ResolverData, 17 | ) => void | Promise; 18 | -------------------------------------------------------------------------------- /src/typings/auth-checker.ts: -------------------------------------------------------------------------------- 1 | import { type ResolverData } from "./resolver-data"; 2 | import { type ClassType } from "./utils"; 3 | 4 | export type AuthCheckerFn = ( 5 | resolverData: ResolverData, 6 | roles: TRoleType[], 7 | ) => boolean | Promise; 8 | 9 | export interface AuthCheckerInterface { 10 | check(resolverData: ResolverData, roles: TRoleType[]): boolean | Promise; 11 | } 12 | 13 | export type AuthChecker = 14 | | AuthCheckerFn 15 | | ClassType>; 16 | 17 | export type AuthMode = "error" | "null"; 18 | -------------------------------------------------------------------------------- /src/typings/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utils"; 2 | export * from "./auth-checker"; 3 | export * from "./Complexity"; 4 | export * from "./legacy-decorators"; 5 | export type { MiddlewareFn, NextFn, MiddlewareInterface } from "./middleware"; 6 | export * from "./resolver-data"; 7 | export * from "./ResolverInterface"; 8 | export * from "./resolvers-map"; 9 | export * from "./SubscribeResolverData"; 10 | export * from "./SubscriptionHandlerData"; 11 | export * from "./subscriptions"; 12 | export * from "./TypeResolver"; 13 | export * from "./ValidatorFn"; 14 | -------------------------------------------------------------------------------- /src/typings/legacy-decorators.ts: -------------------------------------------------------------------------------- 1 | export type ParameterDecorator = ( 2 | target: Object, 3 | propertyKey: string | symbol, // Removed 'undefined' from TS 5.0 4 | parameterIndex: number, 5 | ) => void; 6 | -------------------------------------------------------------------------------- /src/typings/middleware.ts: -------------------------------------------------------------------------------- 1 | import { type ResolverData } from "./resolver-data"; 2 | 3 | export type NextFn = () => Promise; 4 | 5 | export type MiddlewareFn = ( 6 | action: ResolverData, 7 | next: NextFn, 8 | ) => Promise; 9 | 10 | export interface MiddlewareInterface { 11 | use: MiddlewareFn; 12 | } 13 | export type MiddlewareClass = new ( 14 | ...args: any[] 15 | ) => MiddlewareInterface; 16 | 17 | export type Middleware = 18 | | MiddlewareFn 19 | | MiddlewareClass; 20 | -------------------------------------------------------------------------------- /src/typings/resolver-data.ts: -------------------------------------------------------------------------------- 1 | import { type GraphQLResolveInfo } from "graphql"; 2 | 3 | export type ArgsDictionary = Record; 4 | 5 | export interface ResolverData { 6 | root: any; 7 | args: ArgsDictionary; 8 | context: TContextType; 9 | info: GraphQLResolveInfo; 10 | } 11 | -------------------------------------------------------------------------------- /src/typings/subscriptions.ts: -------------------------------------------------------------------------------- 1 | export interface PubSub { 2 | /** 3 | * Publish a value for a given topic. 4 | */ 5 | publish(routingKey: string, ...args: unknown[]): void; 6 | /** 7 | * Subscribe to a topic. 8 | */ 9 | subscribe(routingKey: string, dynamicId?: unknown): AsyncIterable; 10 | } 11 | -------------------------------------------------------------------------------- /src/typings/utils/ClassType.ts: -------------------------------------------------------------------------------- 1 | // Copied from 'type-fest' (https://github.com/sindresorhus/type-fest/blob/main/source/basic.d.ts) 2 | 3 | import { type Constructor } from "./Constructor"; 4 | 5 | /** 6 | Matches a [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). 7 | */ 8 | export type ClassType = Constructor< 9 | T, 10 | Arguments 11 | > & { 12 | prototype: T; 13 | }; 14 | -------------------------------------------------------------------------------- /src/typings/utils/Constructor.ts: -------------------------------------------------------------------------------- 1 | // Copied from 'type-fest' (https://github.com/sindresorhus/type-fest/blob/main/source/basic.d.ts) 2 | 3 | /** 4 | Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). 5 | */ 6 | export type Constructor = new ( 7 | ...arguments_: Arguments 8 | ) => T; 9 | -------------------------------------------------------------------------------- /src/typings/utils/Maybe.ts: -------------------------------------------------------------------------------- 1 | export type Maybe = T | null | undefined; 2 | -------------------------------------------------------------------------------- /src/typings/utils/MaybePromise.ts: -------------------------------------------------------------------------------- 1 | export type MaybePromise = Promise | T; 2 | -------------------------------------------------------------------------------- /src/typings/utils/NonEmptyArray.ts: -------------------------------------------------------------------------------- 1 | // Copied from 'type-fest' (https://github.com/sindresorhus/type-fest/blob/main/source/set-required.d.ts) 2 | 3 | export type NonEmptyArray = readonly [T, ...T[]] | [T, ...T[]]; 4 | -------------------------------------------------------------------------------- /src/typings/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ClassType"; 2 | export * from "./Constructor"; 3 | export * from "./Except"; 4 | export * from "./IsEqual"; 5 | export * from "./Maybe"; 6 | export * from "./MaybePromise"; 7 | export * from "./MergeExclusive"; 8 | export * from "./NonEmptyArray"; 9 | export * from "./SetRequired"; 10 | export * from "./Simplify"; 11 | -------------------------------------------------------------------------------- /src/utils/buildTypeDefsAndResolvers.ts: -------------------------------------------------------------------------------- 1 | import { type GraphQLSchema, printSchema } from "graphql"; 2 | import { type BuildSchemaOptions, buildSchema, buildSchemaSync } from "./buildSchema"; 3 | import { createResolversMap } from "./createResolversMap"; 4 | 5 | function createTypeDefsAndResolversMap(schema: GraphQLSchema) { 6 | const typeDefs = printSchema(schema); 7 | const resolvers = createResolversMap(schema); 8 | return { typeDefs, resolvers }; 9 | } 10 | 11 | export async function buildTypeDefsAndResolvers(options: BuildSchemaOptions) { 12 | const schema = await buildSchema(options); 13 | return createTypeDefsAndResolversMap(schema); 14 | } 15 | 16 | export function buildTypeDefsAndResolversSync(options: BuildSchemaOptions) { 17 | const schema = buildSchemaSync(options); 18 | return createTypeDefsAndResolversMap(schema); 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/graphql-version.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "graphql"; 2 | import semVer from "semver"; 3 | // Avoid '@/' due to 'scripts/version.ts' 4 | import { UnmetGraphQLPeerDependencyError } from "../errors"; 5 | 6 | // This must be kept in sync with 'package.json' 7 | export const graphQLPeerDependencyVersion = "^16.9.0"; 8 | 9 | export function ensureInstalledCorrectGraphQLPackage() { 10 | if (!semVer.satisfies(graphql.version, graphQLPeerDependencyVersion)) { 11 | throw new UnmetGraphQLPeerDependencyError(graphql.version, graphQLPeerDependencyVersion); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export type { BuildSchemaOptions } from "./buildSchema"; 2 | export { buildSchema, buildSchemaSync } from "./buildSchema"; 3 | export { 4 | buildTypeDefsAndResolvers, 5 | buildTypeDefsAndResolversSync, 6 | } from "./buildTypeDefsAndResolvers"; 7 | export type { ContainerType, ContainerGetter } from "./container"; 8 | export { createResolversMap } from "./createResolversMap"; 9 | export type { PrintSchemaOptions } from "./emitSchemaDefinitionFile"; 10 | export { 11 | emitSchemaDefinitionFile, 12 | emitSchemaDefinitionFileSync, 13 | defaultPrintSchemaOptions, 14 | } from "./emitSchemaDefinitionFile"; 15 | export * from "./graphql-version"; 16 | -------------------------------------------------------------------------------- /src/utils/isPromiseLike.ts: -------------------------------------------------------------------------------- 1 | export function isPromiseLike( 2 | value: PromiseLike | TValue, 3 | ): value is PromiseLike { 4 | return value != null && typeof (value as PromiseLike).then === "function"; 5 | } 6 | -------------------------------------------------------------------------------- /tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest/globals": true 4 | }, 5 | "rules": { 6 | "max-classes-per-file": "off", 7 | "class-methods-use-this": "off", 8 | "import/no-extraneous-dependencies": [ 9 | "error", 10 | { 11 | "devDependencies": true 12 | } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/functional/errors/metadata-polyfill.ts: -------------------------------------------------------------------------------- 1 | import { ReflectMetadataMissingError } from "type-graphql"; 2 | import { findType } from "@/helpers/findType"; 3 | import { expectToThrow } from "../../helpers/expectToThrow"; 4 | 5 | describe("Reflect metadata", () => { 6 | it("should throw ReflectMetadataMissingError when no polyfill provided", async () => { 7 | const error = await expectToThrow(() => 8 | findType({ 9 | metadataKey: "design:type", 10 | prototype: {}, 11 | propertyKey: "test", 12 | }), 13 | ); 14 | 15 | expect(error).toBeInstanceOf(ReflectMetadataMissingError); 16 | expect(error.message).toContain("metadata"); 17 | expect(error.message).toContain("polyfill"); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/functional/peer-dependency.ts: -------------------------------------------------------------------------------- 1 | import { ensureInstalledCorrectGraphQLPackage } from "@/utils/graphql-version"; 2 | 3 | describe("`graphql` package peer dependency", () => { 4 | it("should have installed correct version", async () => { 5 | expect(ensureInstalledCorrectGraphQLPackage).not.toThrow(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /tests/helpers/circular-refs/good/CircularRef1.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | // eslint-disable-next-line import/no-cycle 3 | import { CircularRef2 } from "./CircularRef2"; 4 | 5 | let hasModuleFinishedInitialLoad = false; 6 | 7 | @ObjectType() 8 | export class CircularRef1 { 9 | @Field(() => { 10 | if (!hasModuleFinishedInitialLoad) { 11 | throw new Error("Field type function was called synchronously during module load"); 12 | } 13 | return [CircularRef2]; 14 | }) 15 | ref2Field!: any[]; 16 | } 17 | 18 | hasModuleFinishedInitialLoad = true; 19 | -------------------------------------------------------------------------------- /tests/helpers/circular-refs/good/CircularRef2.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | // eslint-disable-next-line import/no-cycle 3 | import { CircularRef1 } from "./CircularRef1"; 4 | 5 | let hasModuleFinishedInitialLoad = false; 6 | 7 | @ObjectType() 8 | export class CircularRef2 { 9 | @Field(() => { 10 | if (!hasModuleFinishedInitialLoad) { 11 | throw new Error("Field type function was called synchronously during module load"); 12 | } 13 | return [CircularRef1]; 14 | }) 15 | ref1Field!: any[]; 16 | } 17 | 18 | hasModuleFinishedInitialLoad = true; 19 | -------------------------------------------------------------------------------- /tests/helpers/circular-refs/wrong/CircularRef1.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | // eslint-disable-next-line import/no-cycle 3 | import { CircularRef2 } from "./CircularRef2"; 4 | 5 | @ObjectType() 6 | export class CircularRef1 { 7 | @Field() 8 | ref2Field!: CircularRef2; 9 | } 10 | -------------------------------------------------------------------------------- /tests/helpers/circular-refs/wrong/CircularRef2.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "type-graphql"; 2 | // eslint-disable-next-line import/no-cycle 3 | import { CircularRef1 } from "./CircularRef1"; 4 | 5 | @ObjectType() 6 | export class CircularRef2 { 7 | @Field() 8 | ref1Field!: CircularRef1; 9 | } 10 | -------------------------------------------------------------------------------- /tests/helpers/customScalar.ts: -------------------------------------------------------------------------------- 1 | import { GraphQLScalarType } from "graphql"; 2 | 3 | export const CustomScalar = new GraphQLScalarType({ 4 | name: "Custom", 5 | parseLiteral: () => "TypeGraphQL parseLiteral", 6 | parseValue: () => "TypeGraphQL parseValue", 7 | serialize: () => "TypeGraphQL serialize", 8 | }); 9 | 10 | export class CustomType {} 11 | 12 | export const ObjectScalar = new GraphQLScalarType({ 13 | name: "ObjectScalar", 14 | parseLiteral: () => ({ 15 | value: "TypeGraphQL parseLiteral", 16 | }), 17 | parseValue: () => ({ 18 | value: "TypeGraphQL parseValue", 19 | }), 20 | serialize: (obj: any) => obj.value, 21 | }); 22 | -------------------------------------------------------------------------------- /tests/helpers/expectToThrow.ts: -------------------------------------------------------------------------------- 1 | export async function expectToThrow(call: () => unknown): Promise { 2 | try { 3 | await call(); 4 | } catch (error: unknown) { 5 | return error as TError; 6 | } 7 | 8 | throw new Error("You've expected a function to throw, but it didn't throw anything."); 9 | } 10 | -------------------------------------------------------------------------------- /tests/helpers/getSampleObjectFieldType.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type IntrospectionNamedTypeRef, 3 | type IntrospectionNonNullTypeRef, 4 | type IntrospectionObjectType, 5 | type IntrospectionSchema, 6 | } from "graphql"; 7 | 8 | export function getSampleObjectFieldType(schemaIntrospection: IntrospectionSchema) { 9 | const sampleObject = schemaIntrospection.types.find( 10 | type => type.name === "SampleObject", 11 | ) as IntrospectionObjectType; 12 | return (fieldName: string) => { 13 | const field = sampleObject.fields.find(it => it.name === fieldName)!; 14 | const fieldType = (field.type as IntrospectionNonNullTypeRef) 15 | .ofType as IntrospectionNamedTypeRef; 16 | return fieldType; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /tests/helpers/getTypeField.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type IntrospectionField, 3 | type IntrospectionInterfaceType, 4 | type IntrospectionObjectType, 5 | } from "graphql"; 6 | 7 | export function getTypeField( 8 | type: IntrospectionObjectType | IntrospectionInterfaceType, 9 | fieldName: string, 10 | ): IntrospectionField { 11 | return type.fields.find(field => field.name === fieldName)!; 12 | } 13 | -------------------------------------------------------------------------------- /tests/helpers/sleep.ts: -------------------------------------------------------------------------------- 1 | export function sleep(ms: number): Promise { 2 | return new Promise(resolve => { 3 | setTimeout(resolve, ms); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "noUnusedLocals": false, 6 | "emitDecoratorMetadata": true 7 | }, 8 | "include": [".", "../src"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "./build/cjs" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "es2020", 5 | "moduleResolution": "bundler", 6 | "outDir": "./build/esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./build/typings", 5 | "emitDeclarationOnly": true, 6 | "declaration": true, 7 | "removeComments": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | lib/core/metadata.js 4 | lib/core/MetadataBlog.js 5 | website/translated_docs 6 | website/build/ 7 | website/yarn.lock 8 | website/node_modules 9 | 10 | website/i18n/* 11 | !website/i18n/en.json 12 | -------------------------------------------------------------------------------- /website/blog/assets/logo_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/blog/assets/logo_mini.png -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "npx docusaurus-build", 5 | "prenew-release": "npm run --prefix ../ gen:docs -- --ref v$npm_config_release", 6 | "new-release": "npx docusaurus-version $npm_config_release", 7 | "postnew-release": "git restore ../docs", 8 | "publish-gh-pages": "npx docusaurus-publish", 9 | "rename-version": "npx docusaurus-rename-version", 10 | "start": "npx docusaurus-start" 11 | }, 12 | "devDependencies": { 13 | "docusaurus": "^1.14.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /website/pages/snippets/object-type.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```ts 4 | @ObjectType() 5 | class Recipe { 6 | @Field() 7 | title: string; 8 | 9 | @Field({ nullable: true }) 10 | description?: string; 11 | 12 | @Field(type => [Rate]) 13 | ratings: Rate[]; 14 | 15 | @Field(type => Float, { nullable: true }) 16 | get averageRating() { 17 | const sum = this.ratings.reduce((a, b) => a + b, 0); 18 | return sum / this.ratings.length; 19 | } 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /website/pages/snippets/testability.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```ts 4 | @Resolver(of => Recipe) 5 | export class RecipeResolver { 6 | constructor( 7 | private readonly recipeRepository: Repository, 8 | private readonly rateRepository: Repository, 9 | ) {} 10 | 11 | @Query(returns => Recipe) 12 | async recipe(@Arg("recipeId") recipeId: string) { 13 | return this.recipeRepository.findOneById(recipeId); 14 | } 15 | 16 | @Mutation(returns => Recipe) 17 | async addRecipe(@Arg("recipe") recipeInput: RecipeInput) { 18 | const newRecipe = this.recipeRepository.create(recipeInput); 19 | return this.recipeRepository.save(newRecipe); 20 | } 21 | 22 | @FieldResolver() 23 | ratings(@Root() recipe: Recipe) { 24 | return this.rateRepository.find({ recipeId: recipe.id }); 25 | } 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /website/pages/snippets/typeorm.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```ts 4 | @Entity() 5 | @ObjectType() 6 | export class Rate { 7 | @PrimaryGeneratedColumn() 8 | readonly id: number; 9 | 10 | @Field(type => Int) 11 | @Column({ type: "int" }) 12 | value: number; 13 | 14 | @Field(type => User) 15 | @ManyToOne(type => User) 16 | user: Promise; 17 | 18 | @Field() 19 | @CreateDateColumn() 20 | date: Date; 21 | 22 | @ManyToOne(type => Recipe) 23 | recipe: Promise; 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /website/pages/snippets/validation.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```ts 4 | @InputType() 5 | export class RecipeInput { 6 | @Field() 7 | @MaxLength(30) 8 | title: string; 9 | 10 | @Field({ nullable: true }) 11 | @Length(30, 255) 12 | description?: string; 13 | 14 | @Field(type => [String]) 15 | @MaxArraySize(25) 16 | ingredients: string[]; 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /website/static/css/prism-theme.css: -------------------------------------------------------------------------------- 1 | code { 2 | background: var(--color-code-block); 3 | } 4 | 5 | .token.function { 6 | color: #dcdcaa; 7 | } 8 | 9 | .token.punctuation, 10 | .token.operator, 11 | .token.constant, 12 | .hljs.graphql { 13 | color: #d4d4d4; 14 | } 15 | 16 | .token.keyword, 17 | .token.boolean { 18 | color: #569cd6; 19 | } 20 | 21 | .token.class-name, 22 | .token.builtin { 23 | color: #4ec9b0; 24 | } 25 | 26 | .token.number { 27 | color: #b5cea8; 28 | } 29 | 30 | .token.comment { 31 | color: #6a9955; 32 | } 33 | 34 | .token.string { 35 | color: #ce9178; 36 | } 37 | 38 | .token.regex { 39 | color: #d16969; 40 | } 41 | 42 | .hljs, 43 | .token.attr-name, 44 | .token.property { 45 | color: #9cdcfe; 46 | } 47 | 48 | .hljs.graphql .token.attr-name { 49 | color: #9cdcfe; 50 | } 51 | 52 | .hljs a { 53 | color: inherit !important; 54 | } 55 | 56 | .hljs { 57 | background: rgb(30, 30, 30); 58 | } 59 | -------------------------------------------------------------------------------- /website/static/img/admin-remix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/admin-remix.png -------------------------------------------------------------------------------- /website/static/img/author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/author.jpg -------------------------------------------------------------------------------- /website/static/img/blue_receipt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/blue_receipt.gif -------------------------------------------------------------------------------- /website/static/img/c19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/c19.png -------------------------------------------------------------------------------- /website/static/img/ecad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/ecad.png -------------------------------------------------------------------------------- /website/static/img/ecad_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/ecad_new.png -------------------------------------------------------------------------------- /website/static/img/famety.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/famety.png -------------------------------------------------------------------------------- /website/static/img/fansoria.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/fansoria.png -------------------------------------------------------------------------------- /website/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/favicon.png -------------------------------------------------------------------------------- /website/static/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/favicon/favicon.ico -------------------------------------------------------------------------------- /website/static/img/flatirons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/flatirons.png -------------------------------------------------------------------------------- /website/static/img/gorrion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/gorrion.png -------------------------------------------------------------------------------- /website/static/img/ig-comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/ig-comment.png -------------------------------------------------------------------------------- /website/static/img/intexsoft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/intexsoft.jpg -------------------------------------------------------------------------------- /website/static/img/leofame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/leofame.png -------------------------------------------------------------------------------- /website/static/img/live-graphics-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/live-graphics-system.png -------------------------------------------------------------------------------- /website/static/img/logo-buzzv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/logo-buzzv.png -------------------------------------------------------------------------------- /website/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/logo.png -------------------------------------------------------------------------------- /website/static/img/lovd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/lovd.png -------------------------------------------------------------------------------- /website/static/img/non-stop-casino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/non-stop-casino.png -------------------------------------------------------------------------------- /website/static/img/nongamstopbets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/nongamstopbets.png -------------------------------------------------------------------------------- /website/static/img/play_fortune.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/play_fortune.png -------------------------------------------------------------------------------- /website/static/img/pwmlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/pwmlogo.png -------------------------------------------------------------------------------- /website/static/img/qulix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/qulix.png -------------------------------------------------------------------------------- /website/static/img/sidesmedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/sidesmedia.png -------------------------------------------------------------------------------- /website/static/img/slon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/slon.png -------------------------------------------------------------------------------- /website/static/img/social_followers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/social_followers.png -------------------------------------------------------------------------------- /website/static/img/socialmention.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/socialmention.webp -------------------------------------------------------------------------------- /website/static/img/socialwick-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/socialwick-logo.png -------------------------------------------------------------------------------- /website/static/img/swiss-mentor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/swiss-mentor.png -------------------------------------------------------------------------------- /website/static/img/tiktok-expert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/tiktok-expert.jpg -------------------------------------------------------------------------------- /website/static/img/ts-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/ts-logo.png -------------------------------------------------------------------------------- /website/static/img/use-viral-logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/use-viral-logo.webp -------------------------------------------------------------------------------- /website/static/img/wordhint.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalLytek/type-graphql/d8c465c7e955fdab83bffabe8c1290389def9561/website/static/img/wordhint.jpg -------------------------------------------------------------------------------- /website/versions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "2.0.0-rc.2", 3 | "2.0.0-rc.1", 4 | "2.0.0-beta.6", 5 | "2.0.0-beta.4", 6 | "2.0.0-beta.3", 7 | "1.2.0-rc.1", 8 | "1.1.1", 9 | "1.1.0", 10 | "1.0.0", 11 | "0.17.6", 12 | "0.17.5", 13 | "0.17.4", 14 | "0.17.3", 15 | "0.17.2", 16 | "0.17.1", 17 | "0.17.0", 18 | "0.16.0" 19 | ] 20 | --------------------------------------------------------------------------------