3. Updating the code
3m

Updating the code

We've got the packages we need, so let's start swapping them in. A good place to start is src/index.js - where our AS3 server code is defined!

src/index.js
const { ApolloServer } = require("apollo-server");
const typeDefs = require("./schema");
const resolvers = require("./resolvers");
const TrackAPI = require("./datasources/track-api");
async function startApolloServer(typeDefs, resolvers) {
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
trackAPI: new TrackAPI(),
};
},
});
const { url, port } = await server.listen();
console.log(`
🚀 Server is running
🔉 Listening on port ${port}
📭 Query at ${url}
`);
}
startApolloServer(typeDefs, resolvers);

New imports

First, let's update the line requiring in ApolloServer so that it uses our new @apollo/server package.

src/index.js
- const { ApolloServer } = require("apollo-server");
+ const { ApolloServer } = require("@apollo/server")

As part of the AS4 upgrade, the @apollo/server package consists of a number of new, smaller packages, and we can access those utilities and functions to customize how our server runs. We'll still use the ApolloServer class we've required, but we need an additional function - startStandaloneServer.

We can access startStandaloneServer through a new import, which targets the /standalone directory within the @apollo/server package.

src/index.js
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const typeDefs = require("./schema");
// other imports...

Configuring our server

startStandaloneServer

To integrate the startStandaloneServer function with our existing server implementation, we need to make a few updates.

The first line we can remove is our server.listen() call. We'll no longer listen to our server instance directly, but rather use our startStandaloneServer function instead.

src/index.js
// ... imports
async function startApolloServer(typeDefs, resolvers) {
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
trackAPI: new TrackAPI(),
};
},
});
- const { url, port } = await server.listen();
console.log(`
🚀 Server is running
🔉 Listening on port ${port}
📭 Query at ${url}
`);
}
startApolloServer(typeDefs, resolvers);

In its place, we can await the results of our startStandaloneServer call, passing it our initialized server constant.

src/index.js
// ... imports
async function startApolloServer(typeDefs, resolvers) {
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
trackAPI: new TrackAPI(),
};
},
});
await startStandaloneServer(server);
console.log(`
🚀 Server is running
🔉 Listening on port ${port}
📭 Query at ${url}
`);
}
startApolloServer(typeDefs, resolvers);

From the results of this call, we can pull out the url that our server is running on.

src/index.js
const { url } = await startStandaloneServer(server);

The context function

startStandaloneServer can take a second , which is a configuration object that lets us customize some of our server's features. Let's go ahead and add the curly braces for that configuration object.

src/index.js
const { url } = await startStandaloneServer(server, {
// configuration details
});

Previously, we defined dataSources directly in our ApolloServer class constructor. The data we returned from that function became available to our on their third positional , context.

In AS4, that definition moves to the startStandaloneServer configuration object under a key called context, which is an asynchronous function.

Let's add that key now:

src/index.js
const { url } = await startStandaloneServer(server, {
context: async () => {},
});

Within this function, we'll return an object. This is the object that our receive on their third .

src/index.js
const { url } = await startStandaloneServer(server, {
context: async () => {
return {};
},
});

Note: In AS3, we referred to the third positional that functions receive as context. As a matter of convention in AS4, we now refer to that as contextValue. This is because when we call the context function in the server configuration, the object it returns arrives on our ' third positional .

Now, let's remove the dataSources definition defined in our ApolloServer instantiation. Instead, we can define a dataSources key in the object returned by the context function. We'll set dataSources to an object as well.

src/index.js
// ... imports
const server = new ApolloServer({
typeDefs,
resolvers,
- dataSources: () => {
- return {
- trackAPI: new TrackAPI(),
- };
- },
});
const { url } = await startStandaloneServer(server, {
context: async () => {
return {
+ dataSources: {}
}
}
})

Finally, we can move the trackAPI key, and the instantiation of the TrackAPI class, to the dataSources object.

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

Note: In AS3, dataSources was defined as a function that returned an object containing the various we wanted our to have access to. In AS4, dataSources is set to an object containing these properties instead. Make sure that your dataSources object matches the implementation above!

Hooking up the cache

When we used 3, an initialize function was called for each DataSource class created, passing in the 's cache and context to make them available for use. In 4, we'll handle this ourselves. We don't need our TrackAPI class to have access to any additional context information (like a user authentication token), so instead we'll make sure we're passing it just the cache from our server initialization.

First, we need to get access to the cache. In AS4, this is made available as a read-only on our server. Let's start by destructuring our server to get access to cache.

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

Next, we'll update our instantiation of the TrackAPI class, passing it an object that contains cache.

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

Excellent! That's our configured in AS4! Our track data will now be available to our functions.

Clean up

Here's the current state of our server:

src/index.js
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(typeDefs, resolvers) {
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
🔉 Listening on port ${port}
📭 Query at ${url}
`);
}
startApolloServer(typeDefs, resolvers);

We've done the bulk of the code updates, but there's another tweak we need to make. Right now, our console.log statement is looking for a port . As you can see, we're no longer pulling out this value from our server.

Let's instead define a port at the top of our file, and future-proof our implementation to read from process.env.PORT if the environment our server is running in sets one by default. If no PORT exists, we can set it to a default value of 4000.

const port = process.env.PORT || 4000;

We can ask our server to read in this port value by defining a listen key inside the startStandaloneServer config object.

As the value of the listen, we'll set an object with a port key. And because the name of the key matches the name of the value we want to pass in, we can use the ES6 shorthand syntax and simply set both the key and the value at once with port.

src/index.js
+ const port = process.env.PORT || 4000;
async function startApolloServer(typeDefs, resolvers) {
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async () => {
const { cache } = server;
return {
dataSources: {
trackAPI: new TrackAPI({ cache }),
},
};
},
+ listen: { port },
});
}

That's it for the server updates... but if you run npm start in your terminal, you'll still see some errors! Other parts of our app are still using the older packages from AS3. Let's tackle those next.

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.