GraphQL module definition
Front-Commerce’s GraphQL modules is the mechanism allowing to extend and override any part of the schema defined by other modules. This reference documentation explains the different configuration keys available.
To get started with GraphQL modules, we recommend you to read the Extend the GraphQL schema documentation page.
A Front-Commerce GraphQL module has to export an object containing its definition. This page contains the API different keys available in a GraphQL module’s definition.
namespace
The simplest (but useless) GraphQL module definition only requires an unique
namespace
key:
// A minimal Front-Commerce GraphQL module that basically does nothing
export default {
namespace: "Acme/HelloWorld",
};
This namespace
can be used by other modules as a dependency (see
dependencies
), to ensure another module has been
registered in the application.
modules
(optional)
The modules
key is useful when creating a
meta module. It allows to declare the
submodules to be registered when this module is registered.
It should be an array with a list of submodules to be included in the application.
Example:
import Core from "./core";
import FeatureA from "./feature-a";
import FeatureB from "./feature-b";
export default {
namespace: "Acme/All",
modules: [Core, FeatureA, FeatureB],
};
Submodules dependencies will be resolved independently.
It means that if a meta module A
declares A1
and A2
in its modules
key,
and A1
depends on module B
then there is no guarantee that A2
will be
initialized after B
.
You should make dependencies explicit either in the meta module definition or in each submodule definition. Our goal is to make sure that each module can be used separately as far as possible. Thus, each dependency should be added to the module needing it and not in a meta module.
dependencies
(optional)
A list of module namespaces (see namespace
) which must have been
registered in the application to allow this module to work. Dependencies will be
initialized before this module.
Example:
export default {
namespace: "Acme/HelloWorld",
dependencies: ["Acme/Core"],
// …
};
Modules are sorted using the toposort
library. See
flattenAndReorderModulesUsingDependencies
code and tests for further details.
typeDefs
(optional)
GraphQL type definitions provided by this module. As a developer, this is a contract with the rest of your codebase.
Type definitions must be
GraphQL Schema Definition Language strings,
and can be declared inline or loaded from a .gql
file.
You can create new types, add top level queries (by extending the Query
type),
mutations or extend types from other modules.
// or import typeDefs from './schema.gql';
const typeDefs = `
extend type Query {
"Be polite!"
sayHello (name: String!): Message
}
type Message {
response: String
audio: String
}
`;
export default {
namespace: "Acme/HelloWorld",
typeDefs: typeDefs,
};
Under the hood, all module’s typeDefs
are merged in a single array passed to
graphql-tools'
makeExecutableSchema
function
resolvers
(optional)
This is an object containing all the resolvers providing data for the fields
defined in the schema (usually from your typeDefs
). This is where the
implementation resides.
Resolver map must follow the format documented in GraphQL Tools.
// or import resolvers from './resolvers.js';
const resolvers = {
Query: {
sayHello: (_, { name }) => {
const message = `Hello ${name}`;
return {
message: message,
audio: `https://tts.service.com/q=${message}`,
};
},
},
};
export default {
namespace: "Acme/HelloWorld",
// …
resolvers: resolvers,
};
Under the hood, all module’s resolvers
are merged in a single object (using
lodash.merge passed to graphql-tools'
makeExecutableSchema
function. It is possible to override resolvers declared in another module by
declaring it as a dependency and registering a local resolver.
contextEnhancer
(optional)
The contextEnhancer
module definition key should be a function that
initializes code that will be made available in GraphQL’s context, under the
loaders
key. It is specific to Front-Commerce.
This is where you could construct models or dataloaders that should be used in your GraphQL resolvers. See Slim down resolvers with loaders for more information.
The contextEnhancer
function must return an object whose keys will be merged
with previous modules’. It will be passed a single argument with the following
keys:
req
: the current server requestloaders
: current loaders (from module initialized beforehand)makeDataLoader
: a factory to build a dataloader (seemakeDataLoader
usage)config
: the global configuration
Example:
import MessageLoader from "./loader";
export default {
namespace: "Acme/HelloWorld",
resolvers: {
Query: {
sayHello: (_, { name }, { loaders }) => {
return loaders.Message.load(`Hello ${name}`);
},
},
},
contextEnhancer: ({ req, loaders, makeDataLoader, config }) => {
return {
Message: MessageLoader(makeDataLoader)(config.apiBaseUrl),
};
},
};
remoteSchema
(optional)
The remoteSchema
key allows you to achieve
remote schema stitching in Front-Commerce.
It should be an object with the following keys.
uri
The uri
key is mandatory and must contain the remote GraphQL endpoint.
Example:
export default {
namespace: "Acme/RemoteFeature",
remoteSchema: {
uri: "https://remote-feature.acme.org/graphql",
},
};
By default all queries and mutations are merged with the current schema. A set of default transformations are applied: read the dedicated documentation section for further information.
transforms
(optional)
The transforms
key allows you to optionally manipulate the remote schema
before it is stitched with the existing Front-Commerce schema.
It must be an array of valid
graphql-tools
Schema Transforms,
and will be applied before Front-Commerce’s
default transforms.
Example:
import { FilterRootFields } from "@graphql-tools/wrap";
export default {
namespace: "Acme/RemoteFeature",
remoteSchema: {
uri: "https://remote-feature.acme.org/graphql"
transforms: [
new FilterRootFields(
(operation, rootField) =>
operation === "Query" && rootField === "aRootFieldToExpose"
)
]
}
};
executor
(optional)
Since version 2.0
The executor
key allows you to optionally modify the underlying
executor
for each request sent to the remote schema.
For instance it allows you to add new headers to your requests if the remote schema needs them. This is usually the case when there is an authentication system in your remote service.
You can create your own executor from scratch by following
Creating an executor
from GraphQL Tools or use the
makeExecutor
helper
available in Front-Commerce.
The following example demonstrates how to add an Authorization
header to your
requests.
import makeExecutor from "server/core/graphql/makeExecutor";
const authenticateRequest = (fetchOptions, { context }) => {
const req = (context && context.req) || {};
const authService = makeAuthServiceFromRequest(req);
if (!authService.isAuthenticated()) {
return fetchOptions;
}
return {
...fetchOptions,
headers: {
...(fetchOptions.headers || {}),
Authorization: `Bearer ${authService.getAuthToken()}`,
},
};
};
// […]
export default {
namespace: "Acme/RemoteFeature",
remoteSchema: {
uri: "https://remote-feature.acme.org/graphql",
// […]
executor: makeExecutor(uri, {
fetchOptionsAdapter: authenticateRequest,
}),
},
};
Deprecated fields
linkContextBuilders
(optional)
This feature has been added in version
1.0.0-beta.3
and is deprecated since2.0.0
This option will be ignored if executor
option is defined.
The linkContextBuilders
key allows you to optionally modify the underlying
Apollo’s HTTP Link context
for each request. Please note that the HTTP Link context is different from the
GraphQL Context (even if they share the
same term!).
It should be a list of functions that will enrich the context with the value they return.
One of the most common usage for instance would be to authenticate remote
requests by adding a Authorization
header to requests. Context builders
functions will receive the
Front-Commerce GraphQL context so they
could implement a wide range of logic based on the current HTTP Request or
loaders.
Example:
// […]
const authenticateRequest = (context) => {
const authService = makeAuthServiceFromRequest(context.req || {});
if (authService.isAuthenticated()) {
return {
headers: {
Authorization: `Bearer ${authService.getAuthToken()}`,
},
};
}
return {};
};
// […]
export default {
namespace: "Acme/RemoteFeature",
remoteSchema: {
uri: "https://remote-feature.acme.org/graphql",
// […]
linkContextBuilders: [authenticateRequest],
},
};
apolloLinkHttpOptions
(optional)
This feature has been added in version
2.0.0-rc.0
and is deprecated since2.0.0
This option will be ignored if executor
option is defined.
The apolloLinkHttpOptions
key allows you to customize options passed to the
apollo-link-http
that fetches the schema.
This is especially useful if the remote GraphQL schema is using the GET method for GraphQL queries.
Example:
// […]
export default {
namespace: "Acme/RemoteFeature",
remoteSchema: {
uri: "https://remote-feature.acme.org/graphql",
// […]
apolloLinkHttpOptions: {
useGETForQueries: true,
},
},
};