Giacomo Lamonaco

Jun 17, 2021

GraphQL Service and gateway schema with Cloudflare Workers

In some cases customers may be required to customize the default behaviour of the GraphQL Service. As example, customers may need a modified version of the commerce schema, “extending” the original one with additional types/relation. In this blog article we are going to describe how it would be possible to extend the GraphQL Service using a serverless-based solution.  

Introduction

Since v14.3, Bloomreach provides a GraphQL Service enabling seamless integrations with Commerce platforms. The GraphQL Service consists of a set of APIs that, together with XM SaaS and the Reference React SPA, provides a ready-to-delivery commerce-based solution: developers can finally focus on implementing the desidered commerce experience.

The GraphQL Service comes with a pre-defined schema: below you can find the queries - and the related types/relations - available by default OOTB.

You can zoom-in, it is an SVG :)                     

In some cases customers may be required to customize the default behaviour of the GraphQL Service. As example, customers may need a modified version of the commerce schema, “extending” the original one with additional types/relation. In this blog article we are going to describe how it would be possible to extend the GraphQL Service using a serverless-based solution.  

More specifically, we are going to experiment with the “Schema Wrapping” provided by the graphql-tools library: as explained in their documentation, “Schema wrapping works by creating a new "gateway" schema that simply delegates all operations to the original subschema. A series of transforms are applied that may modify the shape of the gateway schema and all proxied operations; these operational transforms may modify an operation prior to delegation, or modify the subschema result prior to its return”.

It is also worth noting that GraphQL Service is also ready-to-be federated using the Apollo Federation mechanism: this option is out-of-scope in this article. In case you are interested in this topic, you can find some information in this documentation page

In case you are interested in the next sections, these are the requirements:

  • GraphQL Service instance;

  • Workers Account.

Cloudflare  Workers

The main idea is to “deploy” this solution using Cloudflare Workers. If you are not familiar with Cloudflare Workers and CLI tools, please have a look at their Getting Started

First of all, you will need to install the Cloudflare Wrangler CLI:

npm install -g @cloudflare/wrangler

In this example we are using the worker-graphql-server project created by Cloudflare:

wrangler generate my-graphql-server https://github.com/signalnerve/workers-graphql-server

This sample project demonstrates how to run a simple Apollo Server on the edge. In the next paragraph we are going to - slightly - modify this example based on our needs.

If everything has been correctly set, you will be able to run the following command:

wrangler dev

In case the account_id is missing, please have a look at the step 7 of the Getting Started.  If everything went well the worker should listen on http://127.0.0.1:8787.

GraphQL Service gateway

First of all, let’s install the required dependency: 

npm install @graphql-tools/wrap

Replace the handlers/apollo.js source with the following:

const { print } = require('graphql');
const { introspectSchema, wrapSchema } = require('@graphql-tools/wrap');
const { ApolloServer } = require('apollo-server-cloudflare')
const { graphqlCloudflare } = require('apollo-server-cloudflare/dist/cloudflareApollo')

const executor = async ({ document, variables }) => {
const query = print(document);
const fetchResult = await fetch('https://<graphql-service-instance>.bloomreach.io/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'connector': 'brsm',
  },
  body: JSON.stringify({ query, variables }),
});
return fetchResult.json();
};

const createServer = async (graphQLOptions) => {
const schema = wrapSchema({
  schema: await introspectSchema(executor),
  executor,
});
return new ApolloServer({ schema });
}

let server = undefined;
const handler = async (request, graphQLOptions) => {
//lazy init
if (server === undefined) {
  server =  await createServer(graphQLOptions);
}
return graphqlCloudflare(() => server.createGraphQLServerOptions(request))(request)
}

module.exports = handler

We have basically wrapped the GraphQL Service schema: we are now able to apply any supported operations (e.g. Transforms) by the graphql-tools library. As example, if someone desires to change the Item type name to Product, it will be possible to do something like the following: 

...
const typeNameMap = {
  Item: 'Product',
};
...
const schema = wrapSchema({
  schema: originalSchema,
  transforms: [new RenameTypes(name => typeNameMap[name] || name)],
});

In case you are interested in more complex operations - e.g. renaming object fields - you can take a look at the “Schema Wrapping” documentation. 

Once the wrapping is over, please execute - again - the following command (do not forget to stop any running instances):

wrangler dev

Once the worker is up and running, you should be able to trigger a sample query like the example below:

curl --location --request POST 'http://127.0.0.1:8787/' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query{\n  findItemsByKeyword(\n    text:\"\", limit: 1, offset: 0, \n  ) {\n    items{\n      displayName\n      itemId{\n        id,\n        code\n      }\n      description\n      customAttrs {\n        name, values\n      }\n    }\n  }\n}\n","variables":{}}'

You should be able to get the expected result, this time through the Cloudflare Worker.

Conclusions

The GraphQL Service comes with a pre-defined set of APIs and their related behaviour: in some cases customers may be required to customize the default behaviour of the GraphQL Service. This blog article demonstrates how to use Cloudflare Workers and the graphq-tools library to wrap/extend the GraphQL Service schema.