From 28b8d4e93426351da6a310ad6d47203777c48f58 Mon Sep 17 00:00:00 2001 From: Luca Del Puppo Date: Fri, 11 Aug 2023 12:17:18 +0200 Subject: [PATCH 1/3] docs: improve custom directives doc with federation --- docs/custom-directive.md | 95 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/docs/custom-directive.md b/docs/custom-directive.md index f021ee7b..65f53c55 100644 --- a/docs/custom-directive.md +++ b/docs/custom-directive.md @@ -56,10 +56,10 @@ const { mapSchema, getDirective, MapperKind } = require("@graphql-tools/utils"); const PHONE_REGEXP = /(?:\+?\d{2}[ -]?\d{3}[ -]?\d{5}|\d{4})/g; const EMAIL_REGEXP = /([^\s@])+@[^\s@]+\.[^\s@]+/g; -const redactionSchemaTransformer = (schema) => +const redactionSchemaTransformer = schema => mapSchema(schema, { // When parsing the schema we find a FIELD - [MapperKind.FIELD]: (fieldConfig) => { + [MapperKind.FIELD]: fieldConfig => { // Get the directive information const redactDirective = getDirective(schema, fieldConfig, "redact")?.[0]; if (redactDirective) { @@ -77,7 +77,7 @@ const redactionSchemaTransformer = (schema) => case "email": return value.replace(EMAIL_REGEXP, "****@*****.***"); case "phone": - return value.replace(PHONE_REGEXP, (m) => "*".repeat(m.length)); + return value.replace(PHONE_REGEXP, m => "*".repeat(m.length)); default: return value; } @@ -127,3 +127,92 @@ app.register(mercurius, { ## Example We have a runnable example on "example/custom-directive.js" + +# Federation and Custom Directives + +If we are using Federation, we must follow a different approach to create custom directives. + +Schema definitions and transformer use the same approach indicated above. But schema generation and transformation have different implementations. + +## Schema Definition + +The schema definition is the same as the one used in the previous example. + +```js +const schema = ` + directive @upper on FIELD_DEFINITION + + extend type Query { + me: User + } + + type User @key(fields: "id") { + id: ID! + name: String @upper + username: String + }`; +``` + +## Transformer + +Also, the transformer follows the same approach used in the previous example. + +```js +const { mapSchema, getDirective, MapperKind } = require("@graphql-tools/utils"); + +const uppercaseTransformer = schema => + mapSchema(schema, { + [MapperKind.FIELD]: fieldConfig => { + const upperDirective = getDirective(schema, fieldConfig, "upper")?.[0]; + if (upperDirective) { + fieldConfig.resolve = async (obj, _args, _ctx, info) => { + const value = obj[info.fieldName]; + return typeof value === "string" ? value.toUpperCase() : value; + }; + } + }, + }); +``` + +## Generate executable schema + +This section starts to be different. First, we need to create the federation schema using the `buildFederationSchema` function from the `mercurius-federation` library; then, we can use the `makeExecutableSchema` function from the `@graphql-tools/schema` library to create the executable schema. + +```js +const { buildFederationSchema } = require("mercurius-federation"); +const { + printSchemaWithDirectives, + getResolversFromSchema, +} = require("@graphql-tools/utils"); +const { mergeResolvers } = require("@graphql-tools/merge"); +const { makeExecutableSchema } = require("@graphql-tools/schema"); + +const federationSchema = buildFederationSchema(schema); + +const executableSchema = makeExecutableSchema({ + typeDefs: printSchemaWithDirectives(federationSchema), + resolvers: mergeResolvers([ + getResolversFromSchema(federationSchema), + resolvers, + ]), +}); +``` + +## Apply transformations to the executable schema + +To apply the transformation, we have to use the mercurius plugin and pass the options: + +- **schema**: with the executableSchema already generated +- **schemaTransforms**: with the transformer functions + +```js +app.register(mercurius, { + schema: executableSchema, + schemaTransforms: [redactionSchemaTransformer], + graphiql: true, +}); +``` + +## Example + +We have a runnable example in the Federation repo that you can find here [examples/withCustomDirectives.js](https://github.com/mercurius-js/mercurius-federation/tree/main/examples/withCustomDirectives.js). From de528dcea6b15d781cd98449cfe3662468bc56df Mon Sep 17 00:00:00 2001 From: Luca Del Puppo Date: Wed, 16 Aug 2023 11:45:40 +0200 Subject: [PATCH 2/3] docs: improve custom directives doc with federation --- docs/custom-directive.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/custom-directive.md b/docs/custom-directive.md index 65f53c55..eadfb91d 100644 --- a/docs/custom-directive.md +++ b/docs/custom-directive.md @@ -128,15 +128,16 @@ app.register(mercurius, { We have a runnable example on "example/custom-directive.js" -# Federation and Custom Directives +## Federation and Custom Directives -If we are using Federation, we must follow a different approach to create custom directives. +With Federation, we use special syntaxes and custom directives already defined; for instance, `extends` or `@key`. These directives are not defined in the schema but are added by the `buildFederationSchema` function from the `@mercuriusjs/federation` library. Hence, to work with the Federation, we must follow a different approach to maintain the Federation's custom directives and add ours as well. +This approach is not entirely different from the one used before, but some steps are different. -Schema definitions and transformer use the same approach indicated above. But schema generation and transformation have different implementations. +In the following example, we will create a custom directive to uppercase the value of a field in a federated environment. -## Schema Definition +### Schema Definition -The schema definition is the same as the one used in the previous example. +The schema definition is equal to the one used in the previous example. We add the `@upper` directive and we decorate the `name` field with it. ```js const schema = ` @@ -153,9 +154,9 @@ const schema = ` }`; ``` -## Transformer +### Transformer -Also, the transformer follows the same approach used in the previous example. +The transformer follows the same approach used in the previous example. We declare the uppercase transform function and apply it to the field resolver if they have the `@upper` directive. ```js const { mapSchema, getDirective, MapperKind } = require("@graphql-tools/utils"); @@ -174,12 +175,15 @@ const uppercaseTransformer = schema => }); ``` -## Generate executable schema +### Generate executable schema -This section starts to be different. First, we need to create the federation schema using the `buildFederationSchema` function from the `mercurius-federation` library; then, we can use the `makeExecutableSchema` function from the `@graphql-tools/schema` library to create the executable schema. +This section starts to be different. First, we need to create the federation schema using the `buildFederationSchema` function from the `@mercuriusjs/federation` library; then, we can use the `makeExecutableSchema` function from the `@graphql-tools/schema` library to create the executable schema. +Using the `printSchemaWithDirectives`, we can get the schema with all the custom directives applied, and using the `mergeResolvers` function from the `@graphql-tools/merge` library, we can merge the resolvers from the federation schema and the ones we defined. + +Following these steps, we can create our executable schema. ```js -const { buildFederationSchema } = require("mercurius-federation"); +const { buildFederationSchema } = require("@mercuriusjs/federation"); const { printSchemaWithDirectives, getResolversFromSchema, @@ -198,7 +202,7 @@ const executableSchema = makeExecutableSchema({ }); ``` -## Apply transformations to the executable schema +### Apply transformations to the executable schema To apply the transformation, we have to use the mercurius plugin and pass the options: @@ -208,11 +212,11 @@ To apply the transformation, we have to use the mercurius plugin and pass the op ```js app.register(mercurius, { schema: executableSchema, - schemaTransforms: [redactionSchemaTransformer], + schemaTransforms: [uppercaseTransformer], graphiql: true, }); ``` -## Example +### Example We have a runnable example in the Federation repo that you can find here [examples/withCustomDirectives.js](https://github.com/mercurius-js/mercurius-federation/tree/main/examples/withCustomDirectives.js). From 68a2b5c7965bd692676fd6f945ce499ed1feb835 Mon Sep 17 00:00:00 2001 From: Luca Del Puppo Date: Wed, 16 Aug 2023 12:35:52 +0200 Subject: [PATCH 3/3] docs: improve custom directives doc with federation introduction --- docs/custom-directive.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/custom-directive.md b/docs/custom-directive.md index eadfb91d..1c6e6850 100644 --- a/docs/custom-directive.md +++ b/docs/custom-directive.md @@ -130,10 +130,9 @@ We have a runnable example on "example/custom-directive.js" ## Federation and Custom Directives -With Federation, we use special syntaxes and custom directives already defined; for instance, `extends` or `@key`. These directives are not defined in the schema but are added by the `buildFederationSchema` function from the `@mercuriusjs/federation` library. Hence, to work with the Federation, we must follow a different approach to maintain the Federation's custom directives and add ours as well. -This approach is not entirely different from the one used before, but some steps are different. +Because schemas involved in GraphQL federation may use special syntax (e.g. `extends`) and custom directives (e.g. `@key`) that are not available in non-federated schemas, there are some extra steps that need to be run to generate the executable schema, involving the use of `buildFederationSchema` from the `@mercuriusjs/federation` library and `printSchemaWithDirectives` from the `@graphql-tools/utils` library. -In the following example, we will create a custom directive to uppercase the value of a field in a federated environment. +To see how this works, we will go through another example where we create a custom directive to uppercase the value of a field in a federated environment. ### Schema Definition