diff --git a/examples/production-app/graphql/directives.ts b/examples/production-app/graphql/directives.ts index 9778b5ac..69a70371 100644 --- a/examples/production-app/graphql/directives.ts +++ b/examples/production-app/graphql/directives.ts @@ -1,45 +1,31 @@ -import { defaultFieldResolver, GraphQLError, GraphQLSchema } from "graphql"; +import { + defaultFieldResolver, + GraphQLError, + GraphQLFieldConfig, +} from "graphql"; import { Int } from "grats"; import { Ctx } from "../ViewerContext"; -import { getDirective, MapperKind, mapSchema } from "@graphql-tools/utils"; /** * Some fields cost credits to access. This directive specifies how many credits * a given field costs. * - * @gqlDirective cost on FIELD_DEFINITION + * @gqlDirective */ -export function debitCredits(args: { credits: Int }, context: Ctx): void { - if (context.credits < args.credits) { - // Using `GraphQLError` here ensures the error is not masked by Yoga. - throw new GraphQLError( - `Insufficient credits remaining. This field cost ${args.credits} credits.`, - ); - } - context.credits -= args.credits; -} - -type CostArgs = { credits: Int }; - -// Monkey patches the `resolve` function of fields with the `@cost` directive -// to deduct credits from the user's account when the field is accessed. -export function applyCreditLimit(schema: GraphQLSchema): GraphQLSchema { - return mapSchema(schema, { - [MapperKind.OBJECT_FIELD]: (fieldConfig) => { - const costDirective = getDirective(schema, fieldConfig, "cost", [ - "grats", - "directives", - ]); - if (costDirective == null || costDirective.length === 0) { - return fieldConfig; - } - - const originalResolve = fieldConfig.resolve ?? defaultFieldResolver; - fieldConfig.resolve = (source, args, context, info) => { - debitCredits(costDirective[0] as CostArgs, context); - return originalResolve(source, args, context, info); - }; - return fieldConfig; - }, - }); +export function cost( + field: GraphQLFieldConfig, + credits: Int, +): GraphQLFieldConfig { + const originalResolve = field.resolve ?? defaultFieldResolver; + field.resolve = (source, resolverArgs, context, info) => { + if (context.credits < credits) { + // Using `GraphQLError` here ensures the error is not masked by Yoga. + throw new GraphQLError( + `Insufficient credits remaining. This field cost ${credits} credits.`, + ); + } + context.credits -= credits; + return originalResolve(source, resolverArgs, context, info); + }; + return field; } diff --git a/examples/production-app/schema.ts b/examples/production-app/schema.ts index b8535b9d..9a62bca3 100644 --- a/examples/production-app/schema.ts +++ b/examples/production-app/schema.ts @@ -12,6 +12,7 @@ import { createLike as mutationCreateLikeResolver } from "./models/Like"; import { createPost as mutationCreatePostResolver } from "./models/Post"; import { createUser as mutationCreateUserResolver } from "./models/User"; import { GraphQLSchema, GraphQLDirective, DirectiveLocation, GraphQLNonNull, GraphQLInt, GraphQLObjectType, GraphQLList, GraphQLString, GraphQLScalarType, GraphQLID, GraphQLInterfaceType, GraphQLBoolean, GraphQLInputObjectType } from "graphql"; +import { cost } from "./graphql/directives"; export function getSchema(): GraphQLSchema { const DateType: GraphQLScalarType = new GraphQLScalarType({ description: "A date and time. Serialized as a Unix timestamp.\n\n**Note**: The `@specifiedBy` directive does not point to a real spec, but is\nincluded here for demonstration purposes.", @@ -54,7 +55,7 @@ export function getSchema(): GraphQLSchema { return postIdResolver(source); } }, - likes: { + likes: cost({ description: "All the likes this post has received.\n**Note:** You can use this connection to access the number of likes.", name: "likes", type: LikeConnectionType, @@ -72,20 +73,10 @@ export function getSchema(): GraphQLSchema { type: GraphQLInt } }, - extensions: { - grats: { - directives: [{ - name: "cost", - args: { - credits: 10 - } - }] - } - }, resolve(source, args, _context, info) { return source.likes(args, info); } - }, + }, {credits: 10}), publishedAt: { description: "The date and time at which the post was created.", name: "publishedAt", diff --git a/examples/production-app/server.ts b/examples/production-app/server.ts index 622ec04e..a337fb50 100644 --- a/examples/production-app/server.ts +++ b/examples/production-app/server.ts @@ -4,10 +4,8 @@ import { getSchema } from "./schema"; import { VC } from "./ViewerContext"; import { addGraphQLScalarSerialization } from "./graphql/CustomScalars"; import { useDeferStream } from "@graphql-yoga/plugin-defer-stream"; -import { applyCreditLimit } from "./graphql/directives"; let schema = getSchema(); -schema = applyCreditLimit(schema); schema = addGraphQLScalarSerialization(schema); const yoga = createYoga({