Patrick Desjardins Blog
Patrick Desjardins picture from a conference

Apollo Server Playground

Posted on: 2019-01-22

Apollo comes with a handy playground. The playground is simple web portal that allows a rich experience to craft a GraphQL query against the Apollo GraphQL server. The interactive system gives autocomplete, schema discovery and display the response. It is a very valuable tool when developing because you do not need to use any external tool to test the GraphQL solution.

For the third post on the series on GraphQL we will explore the playground. Fortunately, it does not require too much work. However, I got stuck and was astonished to see that some people got in the same trouble I had to go in 2016 -- two years before me. I'll describe how I configured the playground and highlight a pitfall that seems to be common and unfixed by default.

In the previous post, we had a very limited Apollo and Express configuration like the following code.

import express, { Request, Response } from "express";
const app = express();
app.get("/healthcheck", (req: Request, res: Response) => {
  res.status(200).send("ok");
});

const server = new ApolloServer(await apolloServerConfig());
server.applyMiddleware({ app, path: endpoint, cors: true });
app.listen(endpoints.graphQlServerPort, () => {
  console.log("Up-and-running");
});

To keep the code tidy, the portion that instantiate the ApolloServer class into a server instance is extracted into a function. The result is a index.ts file more readable because we will add a few configurations to the instantiation of Apollo.

import express, { Request, Response } from "express";
const app = express();
app.get("/healthcheck", (req: Request, res: Response) => {
  res.status(200).send("ok");
});

installGraph("/api/graph", app).then(() => {
  app.listen(endpoints.graphQlServerPort, () => {
    console.log("Up-and-running");
  });
});

The installGraph function takes two parameters. The first one is the path under the GraphQL is reachable. The string can be to any URL you want. The second one is the Express server. The function contains the two lines that we had before with a small modification concerning the parameter passed to the server.

const server = new ApolloServer(await apolloServerConfig());
server.applyMiddleware({ app, path: endpoint, cors: true });

The configuration function is asynchronous, but does not need to be. I have some configuration that requires to perform a call that is async for authentication purposes. The key idea is that in a single function you will configure all you Apollo GraphQL server. The main configuration is to set the typeDefs and resolvers. For the moment, we can use some fake data for both but later we will connect to read data source like REST services and gRPC services. A simple definition can be of a book with a title and an author. The resolver can return the whol list of book.

import { gql } from "apollo-server-core";

// Type Definition
export const typeDefs = gql`
  type Book {
    title: String
    author: String
  }
  type Query {
    books: [Book]
  }
`;

// Fake data source
export const books = [
  {
    title: "Harry Potter and the Chamber of Secrets",
    author: "J.K. Rowling",
  },
  {
    title: "Jurassic Park",
    author: "Michael Crichton",
  },
];

// Resolver
export const resolvers = {
  Query: {
    books: (obj: null, args: {}, context: {}) => {
      return books;
    },
  },
};

When the resolver and the type definition are coded, you can insert them in the Apollo Server.

const serverConfig: ApolloServerConfig = {
  typeDefs,
  resolvers,
  playground: true,
};

There are few caveats with the code above. The first one is that it has a single type definition and a single resolver. The second is the lack of authentication. We also do not pass any parameter that would give the possibility to fetch a single entity instead of a full list. Finally, the context is not set hence we do not know who is making the request and cannot resolve the information for a particular user. Nevertheless, the playground is activated. By running the code and going to the endpoint passed by parameter, in my case "/api/graph" it is possible to query from any HTTP request but by going into the browser and use the playground.

Example of writing a query in the playground

Summary

This article is a little bit deceiving. Not that I lied, but that I know that few things does not work. For example, with the current implementation, the code does not scale well for GraphQL type definitions or resolvers. The playground works, but if you are using an authenticated API, it will fail. In the next article I will modify the code of this article to make it scales better to have many GraphQL type definitions and resolvers as well as bringing a strongly typed TypeScript context for authentication purposes but also to allow returning from GraphQL information that belong to only the authenticated user.

My Other GraphQL Blog Posts