Skip to main content

Distributed GraphQL with Apollo Server

Your apps will run faster if the APIs they call are physically located close to your end users. This tutorial will show you how to distribute an Apollo Server in multiple locations around the world by using the Section.

You'll first build a small Apollo Server container, push it to Docker Hub, and then deploy it to Section. This tutorial was inspired by an example from PreciousChicken.

note

Before starting, create a new Section Project and then delete the default Deployment and ingress-upstream Service to prepare the project for your new deployment.

Prerequisites

  • You need an account on Docker Hub.
  • You need Docker and Node installed so that you can build a docker image.

Get Setup with Apollo

Install the relevant packages.

mkdir apollo
cd apollo
npm init -y
npm install apollo-server graphql

Create Your Container Image

The container image is Apollo code plus a small amount of static data, built upon node. For a real Apollo Server you'd have it pointing to a database or REST API. But static data is all we want for the minute. Let's start by saving the following code into index.js.

index.js
const { ApolloServer, gql } = require('apollo-server');

const data = {
"beasts": [
{
"id": "md",
"legs": 6,
"binomial": "Musca domestica",
"commonName": "housefly",
},
{
"id": "nr",
"legs": 8,
"binomial": "Neriene radiata",
"commonName": "filmy dome spider",
},
{
"id": "cc",
"legs": 2,
"binomial": "Corvus corone",
"commonName": "carrion crow",
},
{
"id": "fc",
"legs": 4,
"binomial": "Felis catus",
"commonName": "cat",
}
]
};

const typeDefs = gql`
type Beast {
id: ID
legs: Int
binomial: String
commonName: String
}

type Query {
beasts: [Beast]
}
`;

const resolvers = {
Query: {
// Returns array of all beasts.
beasts: () => data.beasts
}
};

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

// The `listen` method launches a web server.
server.listen(4000).then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});

Now let's define the Dockerfile.

Dockerfile
# Uses the node base image with the latest LTS version
FROM node:14.16.0
# Informs Docker that the container listens on the
# specified network ports at runtime
EXPOSE 4000
# Copies index.js and the two package files from the local
# directory to a new app directory on the container
COPY index.js package.json package-lock.json app/
# Changes working directory to the new directory just created
WORKDIR /app
# Installs npm dependencies on container
RUN npm ci
# Command container will actually run when called
CMD ["node", "index.js"]

Build and Publish the Image

Build the Docker image and push it to Docker Hub, substituting YOUR_DOCKERHUB_ACCOUNT accordingly.

docker build -t my-apollo-server-image .
docker tag my-apollo-server-image YOUR_DOCKERHUB_ACCOUNT/my-apollo-server:latest
docker push YOUR_DOCKERHUB_ACCOUNT/my-apollo-server:latest

Create a Kubernetes Deployment for Apollo Server

Next, create a Section deployment for Apollo Server with a file apollo-deployment.yaml, substituting YOUR_DOCKERHUB_ACCOUNT accordingly. This will direct Section to distribute the container you've pushed to Docker Hub.

apollo-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: apollo
name: apollo
spec:
replicas: 1
selector:
matchLabels:
app: apollo
template:
metadata:
labels:
app: apollo
spec:
containers:
- image: YOUR_DOCKERHUB_ACCOUNT/my-apollo-server:latest
imagePullPolicy: Always
name: apollo
resources:
requests:
memory: ".5Gi"
cpu: "500m"
limits:
memory: ".5Gi"
cpu: "500m"

Apply this deployment resource to your Project with either the Kubernetes dashboard or kubectl apply -f apollo-deployment.yaml.

Expose the Service on the Internet

We want to expose the Apollo Server on the Internet. Create ingress-upstream.yaml as defined below.

ingress-upstream.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: ingress-upstream
name: ingress-upstream
spec:
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 4000
selector:
app: apollo
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}

Apply this service resource to your Project with either the Kubernetes dashboard or kubectl apply -f ingress-upstream.yaml.

See the pods running on Section's network with kubectl get pods -o wide. The -o wide switch shows where your GraphQL API is running according to the default AEE location optimization strategy. Your GraphQL API will be optimally deployed according to traffic. In lieu of significant traffic, your deployment will be made to default locations.

Apollo pods

Try kubectl logs POD to see the log message reporting that the server is listening on port 4000.

Finally, follow the instructions that configure DNS and TLS.

Browse Your GraphQL API

Visit https://YOUR_ENVIRONMENT_HOSTNAME in your browser to play in the Apollo Sandbox. You may have multiple pods running in multiple locations, but your chosen hostname will route to the one that is physically closest.

Enter the following query:

{
beasts {
commonName
legs
}
}

You'll see the following result: Apollo Sandbox Beasts