7. Connecting the dots in server-land
2m

We've got our and ready, but they don't know yet how to work together.

is where all the elements we've built previously (the schema, the , and the ) come together in perfect coordination.

Hand-drawn illustration depicting a GraphQL server juggling three components: the schema, resolver function and data sources

In server/src/index.js, where we configured our in Part I, we can now replace our mocks with .

Let's remove the mocks object, as well as all the mocks setup in the ApolloServer constructor and the imports for the mock functions.

const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');
- const { addMocksToSchema } = require('@graphql-tools/mock');
- const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = require('./schema');
- const mocks = {
- Query: () => ({
- tracksForHome: () => [...new Array(6)],
- }),
- Track: () => ({
- id: () => 'track_01',
- title: () => 'Astro Kitty, Space Explorer',
- author: () => {
- return {
- name: 'Grumpy Cat',
- photo: 'https://res.cloudinary.com/dety84pbu/image/upload/v1606816219/kitty-veyron-sm_mctf3c.jpg',
- };
- },
- thumbnail: () => 'https://res.cloudinary.com/dety84pbu/image/upload/v1598465568/nebula_cat_djkt9r.jpg',
- length: () => 1210,
- modulesCount: () => 6,
- }),
- };
async function startApolloServer() {
const server = new ApolloServer({
- schema: addMocksToSchema({
- schema: makeExecutableSchema({ typeDefs }),
- mocks,
- }),
});
const { url } = await startStandaloneServer(server);
console.log(`
🚀 Server is running
📭 Query at ${url}
`);
}
startApolloServer();

We should end up with something that looks like this:

const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const typeDefs = require("./schema");
async function startApolloServer() {
const server = new ApolloServer({});
const { url } = await startStandaloneServer(server);
console.log(`
🚀 Server is running
📭 Query at ${url}
`);
}
startApolloServer();

Next, let's import our resolvers file at the top.

const resolvers = require("./resolvers");

And then add typeDefs and resolvers to the ApolloServer options.

const server = new ApolloServer({
typeDefs,
resolvers,
});

That's the taken care of.

Next, just below our resolvers import, we'll require track-api, our file (extending RESTDataSource), and call it TrackAPI (note the PascalCase convention, as we're dealing with the class here).

const TrackAPI = require("./datasources/track-api");

To connect our server with our TrackAPI, we'll jump down to the startStandaloneServer function. This function takes a second , which is an object for configuring your server's options.

const { url } = await startStandaloneServer(server, {
// TODO: configure server options
});

This is where we'll define a context function that returns an object that all our will share: contextValue (that third positional we talked about earlier!).

Let's set a context property as an async function, which returns an object.

const { url } = await startStandaloneServer(server, {
context: async () => {
// this object becomes our resolver's contextValue, the third positional argument
return {
// TODO
};
},
});

Remember, we want to access the dataSources.trackAPI (and its methods) from the contextValue parameter of our . So let's return an object that allows us to do just that!

We'll set a dataSources property inside the object, which is set to another object. This object will have a trackAPI key, which returns an instance of the TrackAPI class we imported earlier.

const { url } = await startStandaloneServer(server, {
context: async () => {
return {
dataSources: {
trackAPI: new TrackAPI(),
},
};
},
});

Note: Our functions expect to find dataSources.trackAPI on their contextValue, which is why we've defined a property called dataSources here in our server. This particular name isn't a requirement - we chose dataSources as a matter of convention. You can give this property whatever name you'd like, but be sure that you update your functions to access the same property.

One last thing! To take advantage of the RESTDataSource's caching capabilities, we need to pass in the server's cache to our TrackAPI.

Just before we return the contextValue object, let's destructure the cache property from the server. Then, we'll pass in an object to the TrackAPI class, containing that cache property.

const { url } = await startStandaloneServer(server, {
context: async () => {
const { cache } = server;
return {
dataSources: {
trackAPI: new TrackAPI({ cache }),
},
};
},
});

This is what our server configuration will look like when it's finished:

const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const typeDefs = require("./schema");
const resolvers = require("./resolvers");
const TrackAPI = require("./datasources/track-api");
async function startApolloServer() {
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async () => {
const { cache } = server;
return {
dataSources: {
trackAPI: new TrackAPI({ cache }),
},
};
},
});
console.log(`
🚀 Server is running
📭 Query at ${url}
`);
}
startApolloServer();

To learn more about the options that ApolloServer can receive, check out the documentation.

Code Challenge!

We have a class called SpaceCatsAPI, which implements a RESTDataSource. We need to access an instance of this class, which we'll call spaceCatsAPI, from our resolvers. Configure the server below so that our resolvers can access this class through the dataSources.spaceCatsAPI property they receive on their contextValue argument. (Watch out, this is case sensitive!)

Why do we need to return the dataSources object from the server's context function?

Our server is now fully configured to work with live data.

Previous

Share your questions and comments about this lesson

Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.