configure-graphql-module.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import { DynamicModule } from '@nestjs/common';
  2. import { GqlModuleOptions, GraphQLModule, GraphQLTypesLoader } from '@nestjs/graphql';
  3. import { GraphQLUpload } from 'apollo-server-core';
  4. import { extendSchema, printSchema } from 'graphql';
  5. import { GraphQLDateTime } from 'graphql-iso-date';
  6. import GraphQLJSON from 'graphql-type-json';
  7. import { ConfigModule } from '../../config/config.module';
  8. import { ConfigService } from '../../config/config.service';
  9. import { I18nModule } from '../../i18n/i18n.module';
  10. import { I18nService } from '../../i18n/i18n.service';
  11. import { getPluginAPIExtensions } from '../../plugin/plugin-utils';
  12. import { ApiSharedModule } from '../api-internal-modules';
  13. import { IdCodecService } from '../common/id-codec.service';
  14. import { IdEncoderExtension } from '../middleware/id-encoder-extension';
  15. import { TranslateErrorExtension } from '../middleware/translate-errors-extension';
  16. import { generateListOptions } from './generate-list-options';
  17. import { addGraphQLCustomFields } from './graphql-custom-fields';
  18. export interface GraphQLApiOptions {
  19. apiType: 'shop' | 'admin';
  20. typePaths: string[];
  21. apiPath: string;
  22. // tslint:disable-next-line:ban-types
  23. resolverModule: Function;
  24. }
  25. /**
  26. * Dynamically generates a GraphQLModule according to the given config options.
  27. */
  28. export function configureGraphQLModule(
  29. getOptions: (configService: ConfigService) => GraphQLApiOptions,
  30. ): DynamicModule {
  31. return GraphQLModule.forRootAsync({
  32. useFactory: (
  33. configService: ConfigService,
  34. i18nService: I18nService,
  35. idCodecService: IdCodecService,
  36. typesLoader: GraphQLTypesLoader,
  37. ) => {
  38. return createGraphQLOptions(
  39. i18nService,
  40. configService,
  41. idCodecService,
  42. typesLoader,
  43. getOptions(configService),
  44. );
  45. },
  46. inject: [ConfigService, I18nService, IdCodecService, GraphQLTypesLoader],
  47. imports: [ConfigModule, I18nModule, ApiSharedModule],
  48. });
  49. }
  50. async function createGraphQLOptions(
  51. i18nService: I18nService,
  52. configService: ConfigService,
  53. idCodecService: IdCodecService,
  54. typesLoader: GraphQLTypesLoader,
  55. options: GraphQLApiOptions,
  56. ): Promise<GqlModuleOptions> {
  57. // Prevent `Type "Node" is missing a "resolveType" resolver.` warnings.
  58. // See https://github.com/apollographql/apollo-server/issues/1075
  59. const dummyResolveType = {
  60. __resolveType() {
  61. return null;
  62. },
  63. };
  64. return {
  65. path: '/' + options.apiPath,
  66. typeDefs: await createTypeDefs(options.apiType),
  67. include: [options.resolverModule],
  68. resolvers: {
  69. JSON: GraphQLJSON,
  70. DateTime: GraphQLDateTime,
  71. Node: dummyResolveType,
  72. PaginatedList: dummyResolveType,
  73. Upload: GraphQLUpload || dummyResolveType,
  74. SearchResultPrice: {
  75. __resolveType(value: any) {
  76. return value.hasOwnProperty('value') ? 'SinglePrice' : 'PriceRange';
  77. },
  78. },
  79. },
  80. uploads: {
  81. maxFileSize: configService.assetOptions.uploadMaxFileSize,
  82. },
  83. playground: true,
  84. debug: true,
  85. context: (req: any) => req,
  86. extensions: [
  87. () => new TranslateErrorExtension(i18nService),
  88. () => new IdEncoderExtension(idCodecService),
  89. ],
  90. // This is handled by the Express cors plugin
  91. cors: false,
  92. };
  93. /**
  94. * Generates the server's GraphQL schema by combining:
  95. * 1. the default schema as defined in the source .graphql files specified by `typePaths`
  96. * 2. any custom fields defined in the config
  97. * 3. any schema extensions defined by plugins
  98. */
  99. async function createTypeDefs(apiType: 'shop' | 'admin'): Promise<string> {
  100. const customFields = configService.customFields;
  101. const typeDefs = await typesLoader.mergeTypesByPaths(options.typePaths);
  102. let schema = generateListOptions(typeDefs);
  103. schema = addGraphQLCustomFields(schema, customFields);
  104. const pluginSchemaExtensions = getPluginAPIExtensions(configService.plugins, apiType).map(
  105. e => e.schema,
  106. );
  107. for (const documentNode of pluginSchemaExtensions) {
  108. schema = extendSchema(schema, documentNode);
  109. }
  110. return printSchema(schema);
  111. }
  112. }