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.
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],
};
Dependency management: submodules dependencies will be resolved independently.
It means that if a meta module
A
declaresA1
andA2
in itsmodules
key, andA1
depends on moduleB
then there is no guarantee thatA2
will be initialized afterB
.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"],
// …
};
In depth: 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,
};
In depth: under the hood, all module’stypeDefs
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,
};
In depth: under the hood, all module’sresolvers
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)
This feature has been added in version 2.0.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 version1.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 version2.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,
},
},
};