Overview
Our schema is ready—now we need a GraphQL server that can serve it. In this lesson, we will:
- Install Apollo Server and related dependencies
- Create and configure an Apollo Server instance from our schema
- Enable mocked data for schema fields
- Start the server and confirm it runs locally
🛠Backend first steps
On the backend side, our first goal is to create a GraphQL server that can:
- Receive an incoming GraphQL query from our client
- Validate that query against our newly created schema
- Populate the queried schema fields with mocked data
- Return the populated fields as a response
The Apollo Server library helps us implement this server quickly, painlessly, and in a production-ready way.
Adding server dependencies
To get started with our server, we'll need a couple packages first: @apollo/server, graphql and graphql-tag.
- The
@apollo/serverpackage provides a full-fledged, spec-compliant GraphQL server. - The
graphqlpackage provides the core logic for parsing and validating GraphQL queries. - The
graphql-tagpackage provides thegqlutility that we'll use to load our schema file.
From the server/ directory, run the following:
npm install @apollo/server@5 graphql graphql-tag
Note: Apollo Server 5 requires Node.js v20 or later and graphql v16.11.0 or later. We recommend using the latest LTS version of Node.
Implementing Apollo Server
In the server/src/ folder, open index.js.
To create our server, we'll use the @apollo/server package. From that package, we'll only need the named export ApolloServer.
We'll also need to use the startStandaloneServer function, which we can import from the @apollo/server/standalone package.
To bring in the contents of schema.graphql, we'll need a few more imports: gql from graphql-tag, plus Node's fs and path modules so we can read the file from disk.
const { ApolloServer } = require("@apollo/server");const { startStandaloneServer } = require("@apollo/server/standalone");const { readFileSync } = require("fs");const path = require("path");const gql = require("graphql-tag");
gql turns a GraphQL string into the document format Apollo Server expects when working with schemas. We'll pass it the contents of our schema.graphql file.
Just below these imports, we'll add a line that reads the schema file and wraps it with gql:
const typeDefs = gql(readFileSync(path.resolve(__dirname, "./schema.graphql"), {encoding: "utf-8",}));
Next, let's set up an async function called startApolloServer. Inside, we'll create an instance of the ApolloServer class and pass it our typeDefs in its options object:
async function startApolloServer() {const server = new ApolloServer({ typeDefs });}
Note: We're using shorthand property notation with implied keys, because we've named our constant with the matching key (typeDefs).
To start the server, we'll use the startStandaloneServer function, passing it the server we just initialized.
async function startApolloServer() {const server = new ApolloServer({ typeDefs });startStandaloneServer(server);}
The startStandaloneServer function returns a Promise, so we'll await the results of that call, and pull out the url property from the result.
async function startApolloServer() {const server = new ApolloServer({ typeDefs });const { url } = await startStandaloneServer(server);}
We'll also log a nice little message letting us know that our server is indeed up and running!
async function startApolloServer() {const server = new ApolloServer({ typeDefs });const { url } = await startStandaloneServer(server);console.log(`🚀 Server is running📠Query at ${url}`);}
Finally, let's not forget to actually call the startApolloServer function at the bottom of the file!
startApolloServer();
Save your changes. From the terminal, we'll launch our server with npm run start (make sure you're in the server/ folder).
We get the log message and...not much else! We have a running server, but that's it. Floating in the vacuum of localhost space without access to any data, it's a sad and lonely server for now. 😿
Even though our server isn't connected to any data sources yet, it would be great to be able to send the server a test query and get a valid response. Fortunately, ApolloServer provides a way to do exactly that, using mocked data.
Mocking data
To enable mocked data, we'll need to use two new packages: @graphql-tools/mock and @graphql-tools/schema.
Let's go ahead and install them.
npm install @graphql-tools/mock @graphql-tools/schema
At the top, we'll import addMocksToSchema and makeExecutableSchema.
const { addMocksToSchema } = require("@graphql-tools/mock");const { makeExecutableSchema } = require("@graphql-tools/schema");
Then, we'll need to tweak the ApolloServer initialization. Instead of passing it the typeDefs directly, we'll be using the schema property. This property is another way of initializing an Apollo Server, which is useful for building federated subgraphs (more on that in our Voyage series) or if we're using functions like makeExecutableSchema (which we are!).
As the value of the schema property, we'll call the addMocksToSchema function and pass it an object. This object defines its own schema property, and here we'll call the makeExecutableSchema function. Then, we'll pass this function an object containing our typeDefs.
const server = new ApolloServer({schema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs }),}),});
With this code, we're generating an executable schema from our typeDefs, and instructing Apollo Server to populate every queried schema field with a placeholder value (such as Hello World for String fields).
However, Hello World isn't a very realistic value for the title of a track or the URL of an author's picture! To serve mocked data that's closer to reality, we'll define a mocks object. This object contains functions that provide the mocked data we want the server to return for each queried field.
Here's our mocks object:
const mocks = {Track: () => ({id: () => "track_01",title: () => "Astro Kitty, Space Explorer",author: () => {return {name: "Grumpy Cat",photo:"https://res.cloudinary.com/apollographql/image/upload/v1730818804/odyssey/lift-off-api/catstrophysicist_bqfh9n_j0amow.jpg",};},thumbnail: () =>"https://res.cloudinary.com/apollographql/image/upload/v1730818804/odyssey/lift-off-api/nebula_cat_djkt9r_nzifdj.jpg",length: () => 1210,modulesCount: () => 6,}),};
This object defines mock values for all of the fields of a Track object (including the Author object it contains). We pass this object to the ApolloServer constructor like so:
const server = new ApolloServer({schema: addMocksToSchema({schema: makeExecutableSchema({ typeDefs }),mocks,}),});
With mocks enabled, Apollo Server always returns exactly two entries for every list field.
To get more entries at a time, let's say 6, we'll add a Query.tracksForHome to our mocks object and return an Array of that given length like so: [...new Array(6)].
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/apollographql/image/upload/v1730818804/odyssey/lift-off-api/catstrophysicist_bqfh9n_j0amow.jpg",};},thumbnail: () =>"https://res.cloudinary.com/apollographql/image/upload/v1730818804/odyssey/lift-off-api/nebula_cat_djkt9r_nzifdj.jpg",length: () => 1210,modulesCount: () => 6,}),};
Code review
Practice
Create a mock object with a type SpaceCat, an id of spacecat_01, and a title of 'spacecat pioneer'
Up next
Now, with our server loaded with mocked data, how can we run a query on it to test if everything works as expected? In the next lesson, we'll use the Apollo Explorer to build and run test queries seamlessly.
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.