@tinkerstack/graphql-annotation 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,484 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __decorateClass = (decorators, target, key, kind) => {
5
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
6
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
7
+ if (decorator = decorators[i])
8
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
9
+ if (kind && result) __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
13
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
14
+
15
+ // src/modules/core/annotation.decorators.ts
16
+ import { SetMetadata } from "@nestjs/common";
17
+ import { Args } from "@nestjs/graphql";
18
+ import "reflect-metadata";
19
+
20
+ // src/modules/core/annotation.constants.ts
21
+ var ANNOTATION_METADATA = "graphql-annotation";
22
+ var ANNOTATION_RESOLVER_METADATA = "graphql-annotation-resolver";
23
+ var GRAPHQL_ANNOTATION_RESOLVER_METADATA = "graphql-annotation-provider";
24
+
25
+ // src/modules/core/annotation.decorators.ts
26
+ function Annotate(name, opts) {
27
+ const annotationName = name;
28
+ const annotationData = opts?.data;
29
+ return (target, propertyKey, descriptorOrParameterIndex) => {
30
+ switch (typeof descriptorOrParameterIndex) {
31
+ // Method
32
+ case "object": {
33
+ const descriptor = descriptorOrParameterIndex;
34
+ _updateAnnotation(descriptor.value ?? target, (meta) => ({
35
+ ...meta,
36
+ [annotationName]: {
37
+ annotation: annotationName,
38
+ data: annotationData,
39
+ target: {
40
+ type: "method",
41
+ name: propertyKey
42
+ }
43
+ }
44
+ }));
45
+ break;
46
+ }
47
+ // Parameter
48
+ case "number": {
49
+ const paramIndex = descriptorOrParameterIndex;
50
+ const paramName = opts?.parameter;
51
+ if (!paramName)
52
+ throw new Error(
53
+ `Unable to apply annotation "${annotationName}". Parameter name not specified.`
54
+ );
55
+ Args(paramName)(target, propertyKey, paramIndex);
56
+ _updateAnnotation(
57
+ target.constructor,
58
+ (meta) => ({
59
+ ...meta,
60
+ [`${annotationName}@${paramIndex}`]: {
61
+ annotation: annotationName,
62
+ data: annotationData,
63
+ target: {
64
+ type: "parameter",
65
+ paramIndex,
66
+ paramName
67
+ }
68
+ }
69
+ }),
70
+ propertyKey
71
+ );
72
+ break;
73
+ }
74
+ default:
75
+ throw new Error(
76
+ `Unable to attach annotation "${name}". Unsupported target.`
77
+ );
78
+ }
79
+ };
80
+ }
81
+ function _updateAnnotation(target, updateFn, propertyKey) {
82
+ const meta = Reflect.getMetadata(ANNOTATION_METADATA, target) ?? {};
83
+ if (propertyKey)
84
+ Reflect.defineMetadata(
85
+ ANNOTATION_METADATA,
86
+ updateFn(meta),
87
+ target,
88
+ propertyKey
89
+ );
90
+ else Reflect.defineMetadata(ANNOTATION_METADATA, updateFn(meta), target);
91
+ }
92
+ function ResolveAnnotation(name) {
93
+ return (target, property, descriptor) => {
94
+ SetMetadata(ANNOTATION_RESOLVER_METADATA, {
95
+ annotation: name ?? property
96
+ })(target, property, descriptor);
97
+ };
98
+ }
99
+
100
+ // src/modules/core/annotation.loader.ts
101
+ import { stitchSchemas } from "@graphql-tools/stitch";
102
+ import { MapperKind, mapSchema } from "@graphql-tools/utils";
103
+ import { FilterRootFields } from "@graphql-tools/wrap";
104
+ import { Injectable as Injectable2, Logger as Logger2 } from "@nestjs/common";
105
+ import {
106
+ PARAM_ARGS_METADATA
107
+ } from "@nestjs/graphql";
108
+ import { GqlParamtype } from "@nestjs/graphql/dist/enums/gql-paramtype.enum";
109
+ import { GraphQLClient } from "graphql-request";
110
+
111
+ // src/modules/core/annotation.resolver.ts
112
+ import { Inject, Injectable, Logger } from "@nestjs/common";
113
+ import { Field, ObjectType, Query, Resolver } from "@nestjs/graphql";
114
+ import { GraphQLJSONObject } from "graphql-scalars";
115
+ import "reflect-metadata";
116
+ var GraphQLAnnotationMeta = class {
117
+ name;
118
+ resolvers;
119
+ };
120
+ __decorateClass([
121
+ Field(() => String)
122
+ ], GraphQLAnnotationMeta.prototype, "name", 2);
123
+ __decorateClass([
124
+ Field(() => GraphQLJSONObject, { name: "resolvers" })
125
+ ], GraphQLAnnotationMeta.prototype, "resolvers", 2);
126
+ GraphQLAnnotationMeta = __decorateClass([
127
+ ObjectType()
128
+ ], GraphQLAnnotationMeta);
129
+ var GraphQLAnnotationResolver = class {
130
+ constructor(metadataScanner, resolverDiscoveryService, meta) {
131
+ this.metadataScanner = metadataScanner;
132
+ this.resolverDiscoveryService = resolverDiscoveryService;
133
+ this.meta = meta;
134
+ }
135
+ _logger = new Logger("GraphQLAnnotationModule");
136
+ async getGraphQLAnnotations() {
137
+ return {
138
+ ...this.meta,
139
+ resolvers: this.resolverAnnotations
140
+ };
141
+ }
142
+ resolverAnnotations = {};
143
+ async onModuleInit() {
144
+ this.resolverAnnotations = this.compileResolversAnnotations();
145
+ }
146
+ compileResolversAnnotations() {
147
+ const instances = this.resolverDiscoveryService.explore();
148
+ const resolversAnnotations = {};
149
+ for (const { instance } of instances)
150
+ for (const methodName of this.metadataScanner.getAllMethodNames(
151
+ Object.getPrototypeOf(instance)
152
+ )) {
153
+ const annotations = [
154
+ ...Object.values(
155
+ Reflect.getMetadata(ANNOTATION_METADATA, instance[methodName]) ?? {}
156
+ ),
157
+ ...Object.values(
158
+ Reflect.getMetadata(
159
+ ANNOTATION_METADATA,
160
+ instance.constructor,
161
+ methodName
162
+ ) ?? {}
163
+ )
164
+ ];
165
+ if (annotations.length <= 0) continue;
166
+ resolversAnnotations[methodName] = annotations;
167
+ this._logger.log(
168
+ `Discovered annotated resolver "${instance.constructor.name}.${methodName}" with ${annotations.length} annotation(s)`
169
+ );
170
+ }
171
+ return resolversAnnotations;
172
+ }
173
+ };
174
+ __publicField(GraphQLAnnotationResolver, "QUERY_NAME", "_annotations");
175
+ __decorateClass([
176
+ Query(() => GraphQLAnnotationMeta, {
177
+ name: GraphQLAnnotationResolver.QUERY_NAME
178
+ })
179
+ ], GraphQLAnnotationResolver.prototype, "getGraphQLAnnotations", 1);
180
+ GraphQLAnnotationResolver = __decorateClass([
181
+ Resolver(),
182
+ Injectable(),
183
+ __decorateParam(2, Inject(ANNOTATION_RESOLVER_METADATA))
184
+ ], GraphQLAnnotationResolver);
185
+
186
+ // src/modules/core/annotation.utils.ts
187
+ import { buildHTTPExecutor } from "@graphql-tools/executor-http";
188
+ import { schemaFromExecutor } from "@graphql-tools/wrap";
189
+ async function loadGraphQLSchema(url) {
190
+ const executor = buildHTTPExecutor({
191
+ endpoint: url
192
+ });
193
+ return {
194
+ schema: await schemaFromExecutor(executor),
195
+ executor
196
+ // Has to be included.
197
+ };
198
+ }
199
+ function split(array, predicate) {
200
+ const match = [];
201
+ const rest = [];
202
+ array.forEach((item) => {
203
+ const matchedPredicate = predicate(item);
204
+ const target = matchedPredicate ? match : rest;
205
+ target.push(item);
206
+ });
207
+ return [
208
+ match,
209
+ rest
210
+ ];
211
+ }
212
+ function shallowMerge(objects) {
213
+ const res = {};
214
+ objects.forEach((o) => Object.assign(res, o));
215
+ return res;
216
+ }
217
+ function isPlainObject(obj) {
218
+ const prototype = Object.getPrototypeOf(obj);
219
+ return prototype === Object.getPrototypeOf({}) || prototype === null;
220
+ }
221
+
222
+ // src/modules/core/annotation.loader.ts
223
+ import { instanceToPlain } from "class-transformer";
224
+ var GraphQLAnnotatedSchemaLoader = class {
225
+ constructor(resolverDiscoveryService, metadataScanner, externalContextCreator) {
226
+ this.resolverDiscoveryService = resolverDiscoveryService;
227
+ this.metadataScanner = metadataScanner;
228
+ this.externalContextCreator = externalContextCreator;
229
+ }
230
+ _logger = new Logger2("GraphQLAnnotationModule");
231
+ async load(sources) {
232
+ const loadedSources = await this.batchLoadSources(sources);
233
+ const schema = stitchSchemas({
234
+ subschemas: loadedSources.map((s) => s.subschema)
235
+ });
236
+ const annotations = shallowMerge(
237
+ loadedSources.map((s) => s.annotation)
238
+ );
239
+ const annotationResolvers = await this.discoverAnnotationResolvers();
240
+ return this.embedAnnotationResolversToSchema(
241
+ schema,
242
+ annotations,
243
+ annotationResolvers
244
+ );
245
+ }
246
+ async discoverAnnotationResolvers() {
247
+ const annotationResolvers = /* @__PURE__ */ new Map();
248
+ const paramFactory = new GraphQLAnnotationResolverParamsFactory();
249
+ const instances = this.resolverDiscoveryService.explore();
250
+ for (const { instance } of instances)
251
+ for (const methodName of this.metadataScanner.getAllMethodNames(
252
+ Object.getPrototypeOf(instance)
253
+ )) {
254
+ const resolverMeta = Reflect.getMetadata(
255
+ ANNOTATION_RESOLVER_METADATA,
256
+ instance[methodName]
257
+ );
258
+ if (!resolverMeta) continue;
259
+ this._logger.log(
260
+ `Discovered resolver for annotation "${resolverMeta.annotation}"`
261
+ );
262
+ annotationResolvers.set(
263
+ resolverMeta.annotation,
264
+ this.externalContextCreator.create(
265
+ instance,
266
+ instance[methodName],
267
+ methodName,
268
+ PARAM_ARGS_METADATA,
269
+ paramFactory,
270
+ void 0,
271
+ void 0,
272
+ void 0,
273
+ "graphql"
274
+ )
275
+ );
276
+ }
277
+ return annotationResolvers;
278
+ }
279
+ async batchLoadSources(sources) {
280
+ const pending = Object.entries(sources).map(async ([name, url]) => {
281
+ const loadId = `${name}@${url}`;
282
+ try {
283
+ const loaded2 = {
284
+ id: loadId,
285
+ url,
286
+ name,
287
+ subschema: await loadGraphQLSchema(url),
288
+ annotation: await this.loadAnnotations(url)
289
+ };
290
+ loaded2.subschema.transforms = [
291
+ new FilterRootFields(
292
+ (op, fieldName) => !fieldName.endsWith(GraphQLAnnotationResolver.QUERY_NAME)
293
+ )
294
+ ];
295
+ this._logger.log(`Loaded schema for ${loadId}`);
296
+ return loaded2;
297
+ } catch (err) {
298
+ this._logger.error(`Unable to load schema for ${loadId}`);
299
+ throw err;
300
+ }
301
+ });
302
+ const [loaded, failed] = split(
303
+ await Promise.allSettled(pending),
304
+ (q) => q.status === "fulfilled"
305
+ );
306
+ if (failed.length > 0)
307
+ throw new Error(
308
+ `Failed to load ${failed.length} schema(s) from sources.`
309
+ );
310
+ return loaded.map((l) => l.value);
311
+ }
312
+ async loadAnnotations(url) {
313
+ const client = new GraphQLClient(url);
314
+ try {
315
+ const res = await client.rawRequest(`
316
+ query IntrospectAnnotations {
317
+ ${GraphQLAnnotationResolver.QUERY_NAME} {
318
+ name
319
+ resolvers
320
+ }
321
+ }
322
+ `);
323
+ this._logger.log(`Loaded annotations for ${url}`);
324
+ return res.data[GraphQLAnnotationResolver.QUERY_NAME];
325
+ } catch (err) {
326
+ this._logger.warn(`Failed to load annotations at ${url}, assuming empty`);
327
+ return {};
328
+ }
329
+ }
330
+ async embedAnnotationResolversToSchema(schema, annotations, annotationResolvers) {
331
+ return mapSchema(schema, {
332
+ [MapperKind.ROOT_FIELD]: (fieldSchema, name, type) => {
333
+ const fieldAnnotations = (annotations.resolvers[name] ?? []).map((info) => ({
334
+ ...info,
335
+ resolver: annotationResolvers.get(info.annotation)
336
+ // NOTE: should be validated in filter
337
+ })).filter((info) => {
338
+ const annotationHasResolver = info.resolver !== void 0;
339
+ if (!annotationHasResolver)
340
+ this._logger.warn(
341
+ `Found unhandled annotation "${info.annotation}", skipping linking process`
342
+ );
343
+ return annotationHasResolver;
344
+ });
345
+ if (fieldAnnotations.length <= 0) return fieldSchema;
346
+ for (const annotation of fieldAnnotations)
347
+ if (annotation.target.type === "parameter" && fieldSchema.args)
348
+ delete fieldSchema.args[annotation.target.paramName];
349
+ const defaultResolver = fieldSchema.resolve;
350
+ fieldSchema.resolve = async function(parent, args, context, info) {
351
+ const annotationCallbacks = await Promise.all(
352
+ fieldAnnotations.map(async (annotation) => ({
353
+ annotation,
354
+ return: await annotation.resolver(
355
+ ...[
356
+ info.rootValue,
357
+ annotation.data,
358
+ context,
359
+ info
360
+ ]
361
+ )
362
+ }))
363
+ );
364
+ const resolvedParams = annotationCallbacks.reduce(
365
+ (params, call) => {
366
+ if (call.annotation.target.type !== "parameter") return params;
367
+ const returnedClassType = typeof call.return === "object" && !isPlainObject(call.return);
368
+ const returnValue = returnedClassType ? instanceToPlain(call.return) : call.return;
369
+ params[call.annotation.target.paramName] = returnValue;
370
+ return params;
371
+ },
372
+ {}
373
+ );
374
+ return defaultResolver(
375
+ parent,
376
+ { ...args, ...resolvedParams },
377
+ context,
378
+ info
379
+ );
380
+ };
381
+ return fieldSchema;
382
+ }
383
+ });
384
+ }
385
+ };
386
+ GraphQLAnnotatedSchemaLoader = __decorateClass([
387
+ Injectable2()
388
+ ], GraphQLAnnotatedSchemaLoader);
389
+ var GraphQLAnnotationResolverParamsFactory = class {
390
+ // This param factory extends graphql's own just with different inputs from annotations instead.
391
+ // https://github.com/nestjs/graphql/blob/master/packages/graphql/lib/factories/params.factory.ts#L9
392
+ exchangeKeyForValue(type, possibleKey, argsContext) {
393
+ if (!argsContext) return null;
394
+ console.log(type, possibleKey, argsContext);
395
+ const args = {
396
+ parentValue: argsContext[0],
397
+ data: argsContext[1],
398
+ context: argsContext[2],
399
+ info: argsContext[3]
400
+ };
401
+ switch (type) {
402
+ case GqlParamtype.ROOT:
403
+ return args.parentValue;
404
+ case GqlParamtype.ARGS:
405
+ return possibleKey && args.data ? args.data[possibleKey] : args.data;
406
+ case GqlParamtype.CONTEXT:
407
+ return possibleKey && args.context ? args.context[possibleKey] : args.context;
408
+ case GqlParamtype.INFO:
409
+ return possibleKey && args.context ? args.info[possibleKey] : args.info;
410
+ default:
411
+ return null;
412
+ }
413
+ }
414
+ };
415
+
416
+ // src/modules/core/annotation.module.ts
417
+ import { Module } from "@nestjs/common";
418
+ import { DiscoveryModule } from "@nestjs/core";
419
+
420
+ // src/modules/core/resolver.explorer.ts
421
+ import { Injectable as Injectable3 } from "@nestjs/common";
422
+ import { RESOLVER_TYPE_METADATA } from "@nestjs/graphql";
423
+ var ResolverDiscoveryService = class {
424
+ constructor(discoveryService) {
425
+ this.discoveryService = discoveryService;
426
+ }
427
+ explore() {
428
+ const wrappedProviders = this.discoveryService.getProviders();
429
+ return wrappedProviders.flatMap((wrapped) => {
430
+ const { instance } = wrapped;
431
+ if (!instance) return [];
432
+ const isResolver = Reflect.hasMetadata(
433
+ RESOLVER_TYPE_METADATA,
434
+ instance.constructor
435
+ );
436
+ if (!isResolver) return [];
437
+ return wrapped;
438
+ });
439
+ }
440
+ };
441
+ ResolverDiscoveryService = __decorateClass([
442
+ Injectable3()
443
+ ], ResolverDiscoveryService);
444
+
445
+ // src/modules/core/annotation.module.ts
446
+ var GraphQLAnnotationModule = class {
447
+ static forRoot(opts) {
448
+ if (opts?.serveAnnotations)
449
+ return {
450
+ global: true,
451
+ module: GraphQLAnnotationModule,
452
+ imports: [DiscoveryModule],
453
+ providers: [
454
+ ResolverDiscoveryService,
455
+ GraphQLAnnotatedSchemaLoader,
456
+ {
457
+ provide: ANNOTATION_RESOLVER_METADATA,
458
+ useValue: opts.serveConfig
459
+ },
460
+ GraphQLAnnotationResolver
461
+ ],
462
+ exports: [GraphQLAnnotatedSchemaLoader]
463
+ };
464
+ return {
465
+ global: true,
466
+ module: GraphQLAnnotationModule,
467
+ imports: [DiscoveryModule],
468
+ providers: [ResolverDiscoveryService, GraphQLAnnotatedSchemaLoader],
469
+ exports: [GraphQLAnnotatedSchemaLoader]
470
+ };
471
+ }
472
+ };
473
+ GraphQLAnnotationModule = __decorateClass([
474
+ Module({})
475
+ ], GraphQLAnnotationModule);
476
+ export {
477
+ ANNOTATION_METADATA,
478
+ ANNOTATION_RESOLVER_METADATA,
479
+ Annotate,
480
+ GRAPHQL_ANNOTATION_RESOLVER_METADATA,
481
+ GraphQLAnnotatedSchemaLoader,
482
+ GraphQLAnnotationModule,
483
+ ResolveAnnotation
484
+ };
package/nest-cli.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/nest-cli",
3
+ "collection": "@nestjs/schematics",
4
+ "sourceRoot": "src",
5
+ "entryFile": "example/main",
6
+ "compilerOptions": {
7
+ "tsConfigPath": "tsconfig.json"
8
+ }
9
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@tinkerstack/graphql-annotation",
3
+ "version": "0.0.0",
4
+ "description": "Better type-safe-able graphql directives",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "keywords": [],
9
+ "author": "@plasteek",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "@graphql-tools/delegate": "^12.0.4",
13
+ "@graphql-tools/executor-http": "^3.1.0",
14
+ "@graphql-tools/stitch": "^10.1.8",
15
+ "@graphql-tools/utils": "^11.0.0",
16
+ "@graphql-tools/wrap": "^11.1.4",
17
+ "class-transformer": "^0.5.1",
18
+ "graphql-request": "^7.2.0"
19
+ },
20
+ "peerDependencies": {
21
+ "@nestjs/common": "^10 || ^11",
22
+ "@nestjs/core": "^10 || ^11",
23
+ "@nestjs/graphql": "^13",
24
+ "class-transformer": "^0",
25
+ "class-validator": "^0",
26
+ "graphql": "^16",
27
+ "graphql-scalars": "^1",
28
+ "reflect-metadata": "^0"
29
+ },
30
+ "devDependencies": {
31
+ "@apollo/server": "^4.12.2",
32
+ "@as-integrations/fastify": "^3.1.0",
33
+ "@nestjs/apollo": "^13.2.3",
34
+ "@nestjs/common": "^11.1.6",
35
+ "@nestjs/core": "^11.1.3",
36
+ "@nestjs/graphql": "^13.2.3",
37
+ "@nestjs/mapped-types": "^2.1.0",
38
+ "@nestjs/platform-fastify": "^11.1.11",
39
+ "class-validator": "^0.14.2",
40
+ "concurrently": "^9.2.1",
41
+ "eslint": "^9.37.0",
42
+ "graphql": "^16.12.0",
43
+ "graphql-scalars": "^1.24.2",
44
+ "prettier": "^3.6.2",
45
+ "reflect-metadata": "^0.2.2",
46
+ "tslib": "^2.8.1",
47
+ "tsup": "^8.5.0",
48
+ "typescript": "^5.9.3"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup",
52
+ "dev": "nest start --watch"
53
+ }
54
+ }
@@ -0,0 +1,38 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { GraphQLModule } from "@nestjs/graphql";
3
+
4
+ import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
5
+ import { ExampleConsumerModule } from "./consumer.example";
6
+ import { GraphQLAnnotatedSchemaLoader } from "@/modules/core/annotation.loader";
7
+ import { GraphQLAnnotationModule } from "@/modules/core/annotation.module";
8
+ import { stitchSchemas } from "@graphql-tools/stitch";
9
+
10
+ @Module({
11
+ imports: [
12
+ ExampleConsumerModule,
13
+ GraphQLAnnotationModule.forRoot(),
14
+ GraphQLModule.forRootAsync<ApolloDriverConfig>({
15
+ driver: ApolloDriver,
16
+ useFactory: (loader: GraphQLAnnotatedSchemaLoader) => ({
17
+ graphiql: true,
18
+ playground: true,
19
+ autoSchemaFile: "./src/example/consumer/consumer.schema.gql",
20
+ async transformSchema(schema) {
21
+ return stitchSchemas({
22
+ subschemas: [
23
+ { schema },
24
+ {
25
+ // Load external annotated schemas
26
+ schema: await loader.load({
27
+ example: "http://localhost:3031/graphql",
28
+ }),
29
+ },
30
+ ],
31
+ });
32
+ },
33
+ }),
34
+ inject: [GraphQLAnnotatedSchemaLoader],
35
+ }),
36
+ ],
37
+ })
38
+ export class ExampleConsumerApp {}
@@ -0,0 +1,57 @@
1
+ import { Annotate, ResolveAnnotation } from "@/modules/core";
2
+ import {
3
+ CanActivate,
4
+ ExecutionContext,
5
+ Injectable,
6
+ Module,
7
+ UseGuards,
8
+ } from "@nestjs/common";
9
+ import {
10
+ Args,
11
+ Context,
12
+ GraphQLExecutionContext,
13
+ Query,
14
+ Resolver,
15
+ } from "@nestjs/graphql";
16
+
17
+ // Export for external
18
+ export type RoleGroups = ("user" | "admin" | "super-admin")[];
19
+ export const ProtectedResolver = (roles: RoleGroups) =>
20
+ Annotate("protected", { data: { roles } });
21
+ export const InjectableArg = (argName: string, resourceType: string) =>
22
+ Annotate("resource", { data: { resourceType }, parameter: argName });
23
+
24
+ @Injectable()
25
+ export class ExampleGuard implements CanActivate {
26
+ canActivate(context: ExecutionContext) {
27
+ // Do something with your execution context
28
+ return true;
29
+ }
30
+ }
31
+
32
+ @Resolver()
33
+ export class ExampleConsumerResolver {
34
+ @Query(() => [String])
35
+ exampleOut() {
36
+ return ["this is from consumer"];
37
+ }
38
+
39
+ @UseGuards(ExampleGuard)
40
+ @ResolveAnnotation("protected")
41
+ verifyUser(context: ExecutionContext, next: () => void) {
42
+ console.log("I AM HERE");
43
+ }
44
+
45
+ // Should use the name
46
+ @ResolveAnnotation("resource")
47
+ resolveResource(
48
+ @Args("resourceType") resourceType: string,
49
+ @Context() context: GraphQLExecutionContext,
50
+ ) {
51
+ console.log(resourceType);
52
+ return "this-is-gateway-provided-username";
53
+ }
54
+ }
55
+
56
+ @Module({ providers: [ExampleConsumerResolver] })
57
+ export class ExampleConsumerModule {}
@@ -0,0 +1,7 @@
1
+ # ------------------------------------------------------
2
+ # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
3
+ # ------------------------------------------------------
4
+
5
+ type Query {
6
+ exampleOut: [String!]!
7
+ }
@@ -0,0 +1,23 @@
1
+ import { NestFactory } from "@nestjs/core";
2
+
3
+ import { FastifyAdapter } from "@nestjs/platform-fastify";
4
+ import { ExampleProducerApp } from "./producer/producer.app";
5
+ import { ConsoleLogger, Type } from "@nestjs/common";
6
+ import { ExampleConsumerApp } from "./consumer/consumer.app";
7
+
8
+ export async function bootstrap(name: string, module: Type, port: number) {
9
+ const app = await NestFactory.create(module, new FastifyAdapter(), {
10
+ logger: new ConsoleLogger({ prefix: name }),
11
+ });
12
+ await app.listen(port);
13
+
14
+ console.log(
15
+ `Listening on localhost:${port}. Playground: http://localhost:${port}/graphql`
16
+ );
17
+ }
18
+
19
+ async function main() {
20
+ await bootstrap("example-producer", ExampleProducerApp, 3031);
21
+ await bootstrap("example-consumer", ExampleConsumerApp, 3030);
22
+ }
23
+ void main();
@@ -0,0 +1,22 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { GraphQLModule } from "@nestjs/graphql";
3
+ import { ExampleProducerModule } from "./producer.example";
4
+
5
+ import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
6
+ import { GraphQLAnnotationModule } from "@/modules/core/annotation.module";
7
+
8
+ @Module({
9
+ imports: [
10
+ GraphQLModule.forRoot<ApolloDriverConfig>({
11
+ driver: ApolloDriver,
12
+ graphiql: true,
13
+ autoSchemaFile: "./src/example/producer/producer.schema.gql",
14
+ }),
15
+ GraphQLAnnotationModule.forRoot({
16
+ serveAnnotations: true,
17
+ serveConfig: { name: "producer-example" },
18
+ }),
19
+ ExampleProducerModule,
20
+ ],
21
+ })
22
+ export class ExampleProducerApp {}