You can combine multiple GraphQL APIs into a unified endpoint using a technique called GraphQL schema stitching. Apollo has an extensive guide on the topic.
I've also written a two part guide how to stitch the Contentful API together with other APIs: Part 1, Part 2.
All of these examples are build around Apollo Server but the same approach works with Apollo Client as well. So you can choose to do the stitching either as part of your API (adding your GraphQL API, Shopify and Contentful to a unified endpoint) or doing it client side. These have both advantages and disadvantages. Server side will generally result less requests to be made from your client while doing it client side will remove the single point of failure that is your stitching proxy.
A basic set-up, using Apollo Server, would be the following:
const {ApolloServer} = require('apollo-server');
const {HttpLink} = require('apollo-link-http');
const {setContext} = require('apollo-link-context');
const {
introspectSchema,
makeRemoteExecutableSchema,
mergeSchemas
} = require('graphql-tools');
const fetch = require('node-fetch');
const {GITHUB_TOKEN, CONTENTFUL_SPACE, CONTENTFUL_TOKEN} = require('./config.json');
const gitHubLink = setContext((request) => ({
headers: {
'Authorization': `Bearer ${GITHUB_TOKEN}`,
}
})).concat(new HttpLink({uri: 'https://api.github.com/graphql', fetch}));
const contentfulLink = setContext((request) => ({
headers: {
'Authorization': `Bearer ${CONTENTFUL_TOKEN}`,
}
})).concat(new HttpLink({uri: `https://graphql.contentful.com/content/v1/spaces/${CONTENTFUL_SPACE}`, fetch}));
async function startServer() {
const gitHubRemoteSchema = await introspectSchema(gitHubLink);
const gitHubSchema = makeRemoteExecutableSchema({
schema: gitHubRemoteSchema,
link: gitHubLink,
});
const contentfulRemoteSchema = await introspectSchema(contentfulLink);
const contentfulSchema = makeRemoteExecutableSchema({
schema: contentfulRemoteSchema,
link: contentfulLink,
});
const schema = mergeSchemas({
schemas: [
gitHubSchema,
contentfulSchema
]
});
const server = new ApolloServer({schema});
return await server.listen();
}
startServer().then(({ url }) => {
console.log(` Server ready at ${url}`);
});
Note that this will just merge the two GraphQL schemas. You might have conflicts if two APIs have the same name for a type or root field.
Two make sure you don't have any conflicts I suggest you transform all schema to give the types and root fields unique prefixes. This could look this:
async function getContentfulSchema() {
const contentfulRemoteSchema = await introspectSchema(contentfulLink);
const contentfulSchema = makeRemoteExecutableSchema({
schema: contentfulRemoteSchema,
link: contentfulLink,
});
return transformSchema(contentfulSchema, [
new RenameTypes((name) => {
if (name.includes('Repository')) {
return name.replace('Repository', 'RepositoryMetadata');
}
return name;
}),
new RenameRootFields((operation, fieldName) => {
if (fieldName.includes('repository')) {
return fieldName.replace('repository', 'repositoryMetadata');
}
return fieldName;
})
]);
}
This will let you use all the apis in a unified manner but you can't query across the different APIs. To do that, you'll have to stitch together fields. I suggest you dive into the links above, they explain this in more depth.