6. Field deprecation
5m

🗑️ Replacing a field in our schema

A common situation for an evolving is to deprecate an existing schema 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 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 is.

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

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

Let's get to it!

Adding a new field

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

In the src folder, in schema.js, we'll add a new in the Track type called durationInSeconds. This 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 for both of these new . Our REST API doesn't provide this durationInSeconds property. That's okay, it doesn't need to match our schema's 1 to 1-- that's the beauty of !

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

Add a new property under the Track object with the same name as the , durationInSeconds. This will be set to our 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 . We can access the length value using the first parameter of the , the parent, because of the resolver chain.

In the durationInSeconds , 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 .

durationInSeconds: ({ length }) => length,

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

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 taken care of. Next, we need to mark the length as deprecated.

↔️ Schema directives

To mark a as deprecated, we'll use . A schema directive is indicated with an @ character, and it decorates a specific symbol in your schema, such as a type or 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 . For our use case, we'll use one of 's default directives: @deprecated.

Illustration showing the syntax for the deprecated directive in a schema

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

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

  • message

  • @

  • fields

  • resolvers

  • @deprecated

  • reason

  • types

  • description

  • !

  • @deleted

🗑️Using the @deprecated directive

Open up the schema.js file again. In the Track type, after the return type of the length , we'll add the @deprecated , 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 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 :

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 the new durationInSeconds 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.

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

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.