Odyssey

Lift-off V: Production & the Schema Registry
deprecated

Overview & SetupWhat is the schema registry?Registering our schemaDeploying Apollo ServerDeploying Apollo ClientField deprecationSchema changes in the registryMonitoring and scaling our API
6. Field deprecation
5m

🗑️ Replacing a field in our schema

A common situation for an evolving graph is to deprecate an existing schema field in favor of a new field. Let's see how to do this in our Catstronauts schema.

As we were going through some code cleanup, we noticed that the length of a Track and a Module is documented to be in minutes. After some digging though, it looks like this is incorrect, and the value returned from our REST API is actually in seconds! The client app is doing the work to transform this number into minutes.

We should fix the description in the schema so that future clients won't be misled. We can also improve our schema to be more explicit about what exactly this field is.

This requires changes to our schema! Here's the plan:

  1. Add a new field to the schema.
  2. Mark the old field as deprecated.
  3. Monitor usage of the old field.
  4. Whenever usage is down and clients have had appropriate time to make their changes, we can safely remove the old field!

Let's get to it!

➕ Adding a new field

First, let's add the new field to our schema. Open up the server repo.

In the src folder, in schema.js, we'll add a new field in the Track type called durationInSeconds. This field name is much clearer. This returns an Int type, and we'll also give it an accurate description.

"The track's full duration, in seconds"
durationInSeconds: Int

We'll do the same for the Module type.

"The module's video duration, in seconds"
durationInSeconds: Int

✍️ Adding resolvers

We'll need a resolver for both of these new fields. Our REST API doesn't provide this durationInSeconds property. That's okay, it doesn't need to match our schema's fields 1 to 1-- that's the beauty of GraphQL!

Open up the resolvers.js file. Let's tackle the Track.durationInSeconds resolver first.

Add a new property under the Track object with the same name as the field, durationInSeconds. This will be set to our resolver function.

const resolvers = {
Query: {
/* query resolvers */
},
Mutation: {
/* mutation resolvers */
},
Track: {
/* Track.author and Track.modules resolvers */
durationInSeconds: () => {},
},
};

Our schema is expecting an Int value to be returned here. We're going to take the length value and map it to this new field. We can access the length value using the first parameter of the resolver, the parent, because of the resolver chain.

In the durationInSeconds resolver, we'll destructure the first parameter for the length property. We don't need any of the other resolver parameters, and we'll return that length value right away in the body of the resolver.

durationInSeconds: ({ length }) => length,

We'll do the same with the Module.durationInSeconds resolver. Inside the resolvers object in the resolvers.js file, let's create a new object for the Module, and add the durationInSeconds resolver:

server/src/resolvers.js
Module: {
durationInSeconds: ({ length }) => length,
},
If our GraphQL server uses a REST API as its only data source, which of the following statements are true?

That's our new field taken care of. Next, we need to mark the length field as deprecated.

↔️ Schema directives

To mark a field as deprecated, we'll use GraphQL schema directives. A schema directive is indicated with an @ character, and it decorates a specific symbol in your schema, such as a type or field definition.

Illustration showing the syntax for a directive in a schema

Our server (or any other system that interacts with our schema) can then perform custom logic for that symbol based on its directives. For our use case, we'll use one of GraphQL's default directives: @deprecated.

Illustration showing the syntax for the deprecated directive in a schema

We apply the @deprecated directive to a field to indicate that the field is... deprecated! We should always pass this directive a reason argument, which indicates why it's being deprecated, and which field a client should use instead. This is useful information for the clients querying your graph.

Illustration showing the syntax for using the deprecated directive in the schema with the reason argument
Schema Directives
A schema directive starts with the 
 
 character and it can appear after particular symbols in the schema, such as
 
 or
 
. An example of a default directive is
 
, which indicates that a field should no longer be used. This directive is often used with the 
 
 argument to specify why the field shouldn't be used, or which field to use instead.

Drag items from this box to the blanks above

  • resolvers

  • types

  • @deleted

  • reason

  • !

  • description

  • @deprecated

  • fields

  • @

  • message

To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.

🗑️Using the @deprecated directive

Open up the schema.js file again. In the Track type, after the return type of the length field, we'll add the @deprecated directive, with the reason as "Use durationInSeconds". We'll also update the description to say "in seconds" (instead of "in minutes", which is incorrect).

The Track.length field should now look like this:

server/src/schema.js
"The track's approximate length to complete, in seconds"
length: Int @deprecated(reason: "Use durationInSeconds")

Let's make the same update to the Module.length field:

server/src/schema.js
"The module's length in seconds"
length: Int @deprecated(reason: "Use durationInSeconds")

And there we go, this was the last change we had to make on the server side!

If you're coding along on your local project, make sure to test your API change before moving on, to confirm you can query the new durationInSeconds field on both track and module.

Code Challenge!

Let's make some schema changes! We're replacing a Spaceship's missionCount field with completedMissionsCount. Deprecate the missionCount field, giving it a reason argument to use completedMissionsCount. Add a new field for completedMissionsCount, which is a nullable Int. Document a helpful description for this field, reusing the same description from missionCount.

Loading...
Loading initial values

After we've confirmed everything works locally, we're ready to push our change to production. You know the drill: add and commit our changes, push them up to GitHub, and wait for Railway to deploy the latest version of our server app.

Task!

You're all set!

Previous
Next

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.

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              GraphQL

              An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.

              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              resolver

              A function that populates data for a particular field in a GraphQL schema. For example:

              const resolvers = {
              Query: {
              author(root, args, context, info) {
              return find(authors, { id: args.id });
              },
              },
              };
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              GraphQL schema

              A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.

              directives

              A GraphQL annotation for a schema or operation that customizes request execution. Prefixed with @ and may include arguments. For example, the @lowerCase directive below can define logic to return the username field in lowercase:

              type User {
              username: String! @lowerCase
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              directives

              A GraphQL annotation for a schema or operation that customizes request execution. Prefixed with @ and may include arguments. For example, the @lowerCase directive below can define logic to return the username field in lowercase:

              type User {
              username: String! @lowerCase
              }
              GraphQL

              An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.

              directive

              A GraphQL annotation for a schema or operation that customizes request execution. Prefixed with @ and may include arguments. For example, the @lowerCase directive below can define logic to return the username field in lowercase:

              type User {
              username: String! @lowerCase
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              argument

              A key-value pair associated with a particular schema field that lets operations pass data to that field's resolver.

              Argument values can be hardcoded as literal values (shown below for clarity) or provided via GraphQL variables (recommended).

              query GetHuman {
              human(id: "200") {
              name
              height(unit: "meters")
              }
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              querying

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              directive

              A GraphQL annotation for a schema or operation that customizes request execution. Prefixed with @ and may include arguments. For example, the @lowerCase directive below can define logic to return the username field in lowercase:

              type User {
              username: String! @lowerCase
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              field

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }

              NEW COURSE ALERT

              Introducing Apollo Connectors

              Connectors are the new and easy way to get started with GraphQL, using existing REST APIs.

              Say goodbye to GraphQL servers and resolvers—now, everything happens in the schema!

              Take the course