Docs
Launch GraphOS Studio

Implementing a subgraph with Apollo Server


This article demonstrates how to create a subgraph for a federated using Node.js and Apollo Server.

To create a using a different language and/or framework, see the list of Federation-compatible subgraph implementations. Note that not all listed libraries support all Federation features.

Defining a subgraph

To be part of a , a must conform to the Apollo Federation subgraph specification, which exposes the 's capabilities to your graph , as well as to tools like Apollo Studio.

Converting an existing monolithic graph into a single is a convenient first step in building a federated . To start, here's a non-federated Apollo Server setup:

index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import gql from 'graphql-tag';
const typeDefs = gql`
type Query {
me: User
}
type User {
id: ID!
username: String
}
`;
const resolvers = {
Query: {
me() {
return { id: '1', username: '@ava' };
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
// Note the top-level await!
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);
index.js
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import gql from 'graphql-tag';
const typeDefs = gql`
type Query {
me: User
}
type User {
id: ID!
username: String
}
`;
const resolvers = {
Query: {
me() {
return { id: '1', username: '@ava' };
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
// Note the top-level await!
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);

Above, we wrap our in the gql tag from the graphql-tag package, converting our into an AST (i.e., DocumentNode). While Apollo Server can accept a string (or DocumentNode) for its typeDefs, the buildSubgraphSchema function below requires the we pass in to be a DocumentNode.

This should look familiar if you've set up Apollo Server before. If you haven't, we recommend you familiarize yourself with the basics before jumping into federation.

Now, let's convert this to a !

1. Install and import @apollo/subgraph

The first step is to install the @apollo/subgraph package in our server project:

npm install @apollo/subgraph

We also need to require the buildSubgraphSchema function from this package in in the file where we initialize ApolloServer (we'll use it later):

index.ts
import { buildSubgraphSchema } from '@apollo/subgraph';
index.js
import { buildSubgraphSchema } from '@apollo/subgraph';

2. Opt in to Federation 2

For a to use new features in Federation 2, its needs to include the following extend schema definition:

import gql from 'graphql-tag';
const typeDefs = gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Query {
me: User
}
type User {
id: ID!
username: String
}
`;
import gql from 'graphql-tag';
const typeDefs = gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Query {
me: User
}
type User {
id: ID!
username: String
}
`;

This definition enables the to use Federation 2 features. Without it, Federation 2 composition assumes that a is using Federation 1, which sets certain defaults for backward compatibility.

As you begin using more federation-specific directives beyond @key and @shareable, you'll need to add those s to the import array shown above.

3. Define an entity

Entities aren't required in a , but they're a core building block of a federated , so it's good to get some practice defining them.

As part of our federated architecture, we want other s to be able to contribute s to the User type. To enable this, we add the @key to the User type's definition to designate it as an entity:

index.ts
const typeDefs = gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Query {
me: User
}
type User
@key(fields: "id") {
id: ID!
username: String
}
`;
index.js
const typeDefs = gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Query {
me: User
}
type User
@key(fields: "id") {
id: ID!
username: String
}
`;

The @key tells the gateway which (s) of the User entity can uniquely identify a particular instance of it. In this case, the gateway can use the single id.

Next, we add a reference resolver for the User entity. A reference tells the gateway how to fetch an entity by its @key s:

index.ts
const resolvers = {
Query: {
me() {
return { id: '1', username: '@ava' };
},
},
User: {
__resolveReference(user, { fetchUserById }) {
return fetchUserById(user.id);
},
},
};
index.js
const resolvers = {
Query: {
me() {
return { id: '1', username: '@ava' };
},
},
User: {
__resolveReference(user, { fetchUserById }) {
return fetchUserById(user.id);
},
},
};

(This example requires defining the fetchUserById function to obtain the appropriate User from our backing data store.)

4. Generate the subgraph schema

Finally, we use the buildSubgraphSchema function from the @apollo/subgraph package to augment our definition with federation support. We provide the result of this function to the ApolloServer constructor:

index.ts
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
// Note the top level await!
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);
index.js
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
// Note the top level await!
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);

The server is now ready to act as a in a federated graph!

Combined example

Here are the snippets above combined (again, note that for this sample to be complete, you must define the fetchUserById function for your ):

index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import gql from 'graphql-tag';
import { buildSubgraphSchema } from '@apollo/subgraph';
const typeDefs = gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Query {
me: User
}
type User @key(fields: "id") {
id: ID!
username: String
}
`;
const resolvers = {
Query: {
me() {
return { id: '1', username: '@ava' };
},
},
User: {
__resolveReference(user, { fetchUserById }) {
return fetchUserById(user.id);
},
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);
index.js
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import gql from 'graphql-tag';
import { buildSubgraphSchema } from '@apollo/subgraph';
const typeDefs = gql`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
type Query {
me: User
}
type User @key(fields: "id") {
id: ID!
username: String
}
`;
const resolvers = {
Query: {
me() {
return { id: '1', username: '@ava' };
},
},
User: {
__resolveReference(user, { fetchUserById }) {
return fetchUserById(user.id);
},
},
};
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers }),
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);

Custom directives

See Custom directives in subgraphs.

Previous
Event reference
Next
@apollo/subgraph reference
Edit on GitHubEditForumsDiscord