1. Project setup
10m

Getting started

Welcome to the Voyage I: Updating to Federation 2 Lab! In this hands-on project, we'll take FlyBy, an app that runs a gateway on Federation 1, and walk through all the steps necessary to upgrade it to use Apollo Federation 2. Along the way, we'll see how these new features improve our experience implementing a federated architecture.

Prerequisites

Before going any further, make sure that you're familiar with basic GraphQL concepts covered in our Lift-off series, as well as Apollo Federation.

Additionally, you should be familiar with the following technologies.

  • NodeJS
  • React

Note: Though we'll cover many of the benefits of upgrading your app's gateway to use Federation 2, this lab does not detail a step-by-step process for how to safely migrate a production application, which should be tested and secured as part of a robust deployment pipeline.

To complete this lab, you'll need an Apollo Studio account. (We've already set up an account as part of our other Odyssey series, so you might have already completed this step! If not, follow the steps outlined below.)

Finally, you'll need to have the Rover CLI installed on your computer to run commands locally. You can check if Rover is installed by running the rover command in any terminal. If you see a list of options and commands for using Rover in the output, you're all set! Otherwise, you can install Rover by running the following command.

curl -sSL https://rover.apollo.dev/nix/latest | sh

Fantastic! That's it for housekeeping. Now let's take a look at how we plan to improve our FlyBy application by upgrading it to use Federation 2.

Introducing FlyBy

FlyBy is a web app that lets users review places they've visited on their intergalactic travels. They can browse a list of all the locations fellow space travelers have visited, see the details and reviews for each location, and submit reviews of their own.

We're jumping into the story on the heels of exciting update. Our team has just upgraded FlyBy with a new feature: activities!

The location UI displaying its associated activities

Each activity takes place at a certain location, and the updates to our client app give us a quick view of which activities are available and where we can go to check them out.

Now travelers can see all the things they can do when visiting our galactic destinations, but this new feature has required significant updates to the GraphQL API.

A location with two attached activities

Want a preview? You can explore the final version of the FlyBy app.

https://odyssey-flyby-lab.netlify.app/

Let's clone the repository and take a closer look at the changes made to support the activities feature.

git clone https://github.com/apollographql/odyssey-voyage-I-lab.git

Project setup

Navigate to the cloned odyssey-voyage-I-lab folder and open up the project in a code editor of your choice.

Reviewing the top-level folders, we see the client side of the app, as well as the gateway server and three subgraphs:

  • subgraph-activities
  • subgraph-locations
  • subgraph-reviews

We also have a final folder that contains a completed version of the full codebase. Feel free to check your work against this folder if you get stuck!

Lastly, you'll notice the project contains a utils folder as well as a package.json at the root. We won't need to modify any of these files, but we've included them to speed up some of our steps throughout the course!

📦 odyssey-voyage-I-lab
┣ 📂 client
┣ 📂 final
┣ 📂 gateway
┣ 📂 subgraph-activities
┣ 📂 subgraph-locations
┣ 📂 subgraph-reviews
┣ 📂 utils
┗ 📄 package.json
┗ 📄 README.md

Let's get things set up by installing some dependencies. In the root of the project, run npm install. This will install all of our dependencies in the client, gateway and subgraph- directories.

odyssey-voyage-I-lab
npm install

Task!

Now that our local code is ready to go, let's take a look at the latest schema changes. We'll look at two different subgraphs: activities and locations.

The new activities subgraph

Let's dive into the subgraph-activities folder and get an idea of the new feature our team has implemented. This subgraph includes files for the schema and resolvers, along with an index.js server file.

📦 subgraph-activities
┣ 📂 datasources
┃ ┗ 📄 activities_data.json
┃ ┣ 📄 ActivitiesApi.js
┣ 📄 activities.graphql
┣ 📄 index.js
┣ 📄 package-lock.json
┣ 📄 package.json
┗ 📄 resolvers.js

In the index.js server file, we can see how we're making use of a data source called ActivitiesAPI to serve up activities data inside of FlyBy.

subgraph-activities/index.js
const server = new ApolloServer({
schema: buildSubgraphSchema({typeDefs, resolvers}),
dataSources: () => {
return {
activitiesAPI: new ActivitiesAPI()
};
}
});

Next, let's take a look at the schema in activities.graphql.

subgraph-activities/activities.graphql
type Query {
"The full list of activities offered on Interplanetary Space Tourism departments locations"
activities: [Activity]
"The details of a specific activity"
activity(id: ID!): Activity
}
enum ActivityTerrain {
TERRESTRIAL
AERIAL
AQUATIC
GALACTIC
}
interface Attraction {
id: ID!
"The name of the attraction"
name: String!
"A short description about the attraction"
description: String!
"The attraction's main photo as a URL"
photo: String!
}
extend type Location @key(fields: "id") {
id: ID! @external
"The activities associated with a location"
activities: [Activity]
}
type Activity implements Attraction @key(fields: "id") {
id: ID!
"The name of the attraction"
name: String!
"A short description about the attraction"
description: String!
"The attraction's main photo as a URL"
photo: String!
"The activity's terrain"
terrain: ActivityTerrain!
"The activity's location"
location: Location
}

There are a few significant features to point out here:

  • The top level Query object defines two fields: activities which returns a list of Activity objects and activity(id: ID!) which returns an Activity specified by id.
  • We define an enum called ActivityTerrain, which consists of four different values we'll use to describe the terrain of a particular activity: TERRESTRIAL, AERIAL, AQUATIC and GALACTIC.
  • We also define an interface called Attraction, which the Activity type implements.
  • The Activity type is an entity, which specifies a primary key field of id.
  • The Activity type uses the ActivityTerrain enum as the return value for a non-nullable field called terrain.
  • The Activity type also specifies a location field, where we are making use of the Location entity.
  • The schema extends the Location entity and adds an activities field, which returns a list of Activity objects.

Note: If you need to brush up on enum or interface types, be sure to check out Side Quest: Intermediate Schema Design, where we discuss these concepts in greater detail.

The locations subgraph

Next up, let's check out the locations.graphql schema file in the subgraph-locations directory.

subgraph-locations/locations.graphql
type Query {
"The full list of locations presented by the Interplanetary Space Tourism department"
locations: [Location!]!
"The details of a specific location"
location(id: ID!): Location
}
enum LocationTerrain {
TERRESTRIAL
AERIAL
AQUATIC
GALACTIC
MAGMATIC
}
interface Attraction {
id: ID!
"The name of the attraction"
name: String!
"A short description about the attraction"
description: String!
"The attraction's main photo as a URL"
photo: String!
}
type Location implements Attraction @key(fields: "id") {
id: ID!
"The name of the location"
name: String!
"A short description about the location"
description: String!
"The location's main photo as a URL"
photo: String!
"The location's terrain"
terrain: LocationTerrain!
}
  • There's an enum called LocationTerrain, which has five possible values: TERRESTRIAL, AERIAL, AQUATIC, GALACTIC, and MAGMATIC.
  • This file also contains the Attraction interface we saw in the activities subgraph, and the Location type implements it.
  • The Location type defines a non-nullable field, terrain, which resolves to one of the five possible values defined in the LocationTerrain enum.

Note: FlyBy has one other subgraph we haven't covered here: the reviews subgraph. Feel free to familiarize yourself with how it's put together by checking out the files in the subgraph-reviews directory!

A quick recap

Let's review the key features we saw in the locations and activities subgraph directories.

  • FlyBy features activities associated with a particular Location.
  • Visitors can write reviews about an Activity in addition to a Location.
  • We've encapsulated shared logic for both Activity and Location types in an interface called Attraction.
  • Both Location and Activity types specify what kind of terrain takes place on - but the LocationTerrain enum includes one value, MAGMATIC, that the ActivityTerrain enum does not!
Our two subgraphs, with a shared interface but differing terrain enums

New features, new problems

These new features are great, but with Federation 1, there are a few problems our developers have been running into.

Terrain enums

For one thing, it's a pain to have to maintain two different lists of terrain types. Our team wants to maintain just one enum called Terrain and include all five possible values between locations and activities, but an important business reason keeps them from doing so!

While FlyBy features locations with a variety of terrains, our legal team is still trying to wrangle all the waivers and red tape to roll out activities that take place on MAGMATIC terrain. If we decided to define the same enum in multiple subgraphs, we would need to make sure that both definitions were identical, or we would run into composition errors!

The locations subgraph and activities subgraph both define a Terrain enum, but the possible values do not match between subgraphs. With Federation 1, this results in a composition error.

For this reason, and to keep the schema accurate and reflective of business rules, our team has defined a separate enum for each subgraph's permitted terrains.

Field repetition

This leads right into the next problem our team has identified: because Location.terrain and Activity.terrain resolve to different enum types, the Location and Activity types both have to define their terrain fields separately. That means our team can't move the terrain field into the Attraction interface, even though both Location and Activity implement it.

That's a bit inconvenient. But as the team looks down the product pipeline, they can see things becoming even more complicated.

Planning new features

The next feature they plan to tackle is implementing a Stats type to show visitors statistics about the temperature, gravity, and other details for a particular location or activity. But not all Stats fields make sense for both locations and activities! For instance, a Location doesn't necessarily need to specify a minimumAge, but that's a business requirement for each Activity!

The Stats type with fields varying in nullability and applicability to locations and activities

And on top of juggling these problems and planning out new features, our teams are still busy maintaining their own subgraphs, responding to client feedback, and working with the frontend. Phew.

Up next

The silver lining? You're here to help! In the coming lessons, we'll see the flexibility Apollo Federation 2 brings to how we implement entities and shared types (such as interfaces and enums) between our subgraphs. By upgrading FlyBy's gateway, we'll accelerate the team's development lifecycle and make these problems they're facing a thing of the past!

Next