Overview
Let's begin with the first change we want to make to our supergraph.
In this lesson, we will:
- Learn the process for replacing a field in our schema
- Learn what a GraphQL directive is used for
- Use the
@deprecateddirective for a field in our schema
The schema so far
A common situation for an evolving graph is to deprecate an existing field in the schema in favor of a new field. Let's investigate one such situation in our Poetic Plates supergraph.
Here's what the schema looks like for the Ingredient type:
type Ingredient {id: ID!"Display text for the ingredient"text: String"The name of an ingredient"name: String"A potential substitute for the ingredient, if available"substitute: String}
Users have shared feedback about their confusion related to the text and name fields of an ingredient.
Let's take a look at what values those fields return. Using your supergraph's Explorer, run the following query to retrieve the list of ingredients for a random recipe.
query GetRandomRecipeIngredients {randomRecipe {ingredients {textname}}}
Depending on which recipe you received randomly, your response may be different, but here's a snippet of what we received:
{"data": {"randomRecipe": {"ingredients": [{ "text": "1 cup onions, medium diced", "name": "onions" },{ "text": "2 tbsp olive oil", "name": "olive oil" }// ...]}}}
Can you see the difference between text and name?
It looks like the text field returns a longer, more detailed version about the ingredient. The name field simply returns what the ingredient is, without any additional details like quantity or how it's prepared.
Digging deeper into each field's description in the schema, it isn't clear from the documentation that this is what users should expect. So we can definitely understand the confusion about which field they should be using for their purposes.
We should fix the description in the schema so that it's more clear what each field is expected to return. We can also improve our schema to be more explicit about what exactly the text field returns. Maybe there's a better name for this field!
This requires changes to our schema, but we don't want to remove the text field and replace it with a new one all in one go. That would break existing queries clients are sending, and we don't want to alarm anyone!
So here's the plan:
- Add a new field to the schema.
- Mark the old field as deprecated.
- Monitor usage of the old field.
- 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 to the schema
First, let's add the new field to our schema.
Open up the recipes subgraph repostiory in your code editor and navigate to the
schema.graphqlfile.Find the
Ingredienttype, and add a new field calleddetailedDescription. This field name is much clearer thantext! This returns aStringtype, and we'll also give it an accurate description, complete with an example.schema.graphql"""A detailed description of the ingredient needed in the recipe.Usually in the format of: <QUANTITY> <INGREDIENT NAME>, <SPECIAL INSTRUCTIONS>.For example: 1 cup onions, medium diced"""detailedDescription: String
Adding a new resolver
We'll need a resolver for this new field. Our data source doesn't provide this detailedDescription 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
src/resolvers/Ingredient.jsfile.Add a new property under the
Ingredientobject with the same name as the field,detailedDescription. This will be set to our resolver function.src/resolvers/Ingredient.jsmodule.exports = {Ingredient: {detailedDescription: () => {},},};Remember, this field will be the exact same value as the
textfield. So we'll want to take thattextvalue and return it from thedetailedDescriptionresolver.We can access the
textvalue using the first parameter of the resolver, theparent, because of the resolver chain.In the
detailedDescriptionresolver, we'll destructure the first parameter (parent) for thetextproperty. We don't need any of the other resolver parameters, and we'll return thattextvalue right away in the body of the resolver.src/resolvers/Ingredient.jsdetailedDescription: ({ text }) => text,That's our new field taken care of!
Testing our changes
Let's make sure that the new schema addition is working properly.
If your server isn't running, you can start it with npm run dev. This will start the server at http://localhost:4001. Let's head over there in the browser to test out a query with Sandbox.
We should see the new field detailedDescription show up on the Documentation panel on the left.
Let's try this query that retrieves a random recipe and its ingredients: both the text and detailedDescription fields.
query GetRandomRecipeIngredients {randomRecipe {ingredients {textdetailedDescription}}}
When we get data back, we should see that the values for both the text and detailedDescription fields are the same.
Everything's working as expected! Onwards with our plan, we need to mark the text field as deprecated.
The @deprecated directive
For our use case, we'll use one of GraphQL's default directives: @deprecated.

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.

Using the @deprecated directive
Open up the
schema.graphqlfile again.Find the
Ingredienttype'stextfield. After the return type of thetextfield, we'll add the@deprecateddirective, with thereasonas"Use detailedDescription".The
Ingredient.textfield should now look like this:schema.graphql"Display text for the ingredient"text: String @deprecated(reason: "Use detailedDescription")
And there we go!
Testing our changes
So what's changed? Let's find out! Your server should still be running and pulling the latest changes (if not, make sure you run npm run dev again). Head back over to http://localhost:4001.
Looking at the same query we had earlier, we can now see a couple changes!
First, the text field is showing up with a yellow squiggly underline, and if we hover over it, we'll see why! The field is deprecated, just like we marked in our schema. We can also see the same message on the Documentation panel on the left.
We can still run the query with no issues, but the warning is meant to let us know that we really shouldn't be including the text field in our queries anymore.
Practice
Drag items from this box to the blanks above
types
@deletedfields
@reason!descriptionmessage@deprecatedresolvers
Key takeaways
- To replace a field in our schema, we should:
- Add the new field
- Mark the old field as deprecated
- Monitor usage of the old field
- Whenever usage is down and clients have had appropriate time to make their changes, we can safely remove the old field!
- A schema directive is indicated with an
@character, and it decorates a specific symbol in your schema, such as a type or field definition. - To deprecate a field, we use the default GraphQL directive
@deprecatedand thereasonargument to let our graph consumers know why and which field to use instead. - We can still run queries with deprecated fields, but the deprecation status is a cue for us to stop using that field in existing queries.
Up next
We've completed the first two steps of our plan, but that was all in our local environment. We need to let GraphOS know about these changes!
In the next two lessons, we'll take a look at how we can land these changes safely and confidently using schema checks and launches.
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.