diff --git a/src/components/marked/swapi-schema.tsx b/src/components/marked/swapi-schema.tsx
index 0f56f9be3e..8cecef578c 100644
--- a/src/components/marked/swapi-schema.tsx
+++ b/src/components/marked/swapi-schema.tsx
@@ -31,6 +31,9 @@ const typeDefs = /* GraphQL */ `
"The mutation type, represents all updates we can make to our data"
type Mutation {
createReview(episode: Episode, review: ReviewInput!): Review
+ rateFilm(episode: Episode!, rating: FilmRating!): Film
+ updateHumanName(id: ID!, name: String!): Human
+ deleteStarship(id: ID!): ID
}
"The episodes in the Star Wars trilogy"
@@ -45,6 +48,24 @@ const typeDefs = /* GraphQL */ `
JEDI
}
+ "A personal rating for a Star Wars episode"
+ enum FilmRating {
+ "Negative rating"
+ THUMBS_DOWN
+
+ "Positive rating"
+ THUMBS_UP
+ }
+
+ "A film from the Star Wars trilogy"
+ type Film {
+ "The Star Wars episode portrayed in the film"
+ episode: Episode!
+
+ "The authenticated user's rating of the film"
+ viewerRating: FilmRating
+ }
+
"A character from the Star Wars universe"
interface Character {
"The ID of the character"
@@ -365,6 +386,24 @@ const resolvers = {
},
Mutation: {
createReview: (root, { episode, review }) => review,
+ rateFilm: (root, { episode, rating }) => ({
+ episode,
+ viewerRating: rating,
+ }),
+ updateHumanName: (root, { id, name }) => {
+ const human = humanData[id]
+ if (!human) {
+ throw new Error("Human not found")
+ }
+ return { ...human, name }
+ },
+ deleteStarship: (root, { id }) => {
+ const starship = getStarship(id)
+ if (!starship) {
+ throw new Error("Starship not found")
+ }
+ return id
+ },
},
Character: {
__resolveType(data, context, info) {
diff --git a/src/pages/learn/_meta.ts b/src/pages/learn/_meta.ts
index f9170ae997..8ea08ecf6a 100644
--- a/src/pages/learn/_meta.ts
+++ b/src/pages/learn/_meta.ts
@@ -4,8 +4,10 @@ export default {
title: "Learn",
},
index: "Introduction",
- queries: "Queries and Mutations",
schema: "Schemas and Types",
+ queries: "",
+ mutations: "",
+ subscriptions: "",
validation: "",
execution: "",
response: "",
diff --git a/src/pages/learn/mutations.mdx b/src/pages/learn/mutations.mdx
new file mode 100644
index 0000000000..641c83c2af
--- /dev/null
+++ b/src/pages/learn/mutations.mdx
@@ -0,0 +1,163 @@
+import { Callout } from "nextra/components"
+
+# Mutations
+
+
Learn how to modify data with a GraphQL server
+
+Most discussions of GraphQL focus on data fetching, but any complete data platform needs a way to modify server-side data as well.
+
+In REST, any request might cause some side-effects on the server, but by convention, it's suggested that one doesn't use `GET` requests to modify data. GraphQL is similar—technically any field resolver could be implemented to cause a data write—but the [GraphQL specification states](https://spec.graphql.org/draft/#sel-GANRNDAB6DBmMn6D) that "the resolution of fields other than top-level mutation fields must always be side effect-free and idempotent." Thus, for any spec-compliant GraphQL schemas, only the top-level fields in mutation operations are allowed to cause side effects.
+
+On this page, you'll learn how to use mutation operations to write data using GraphQL, and do so in a way that supports client use cases.
+
+
+All of the features of GraphQL operations that apply to queries also apply to mutations, so review the [Queries](/learn/queries/) page first before proceeding.
+
+
+## Add new data
+
+When creating new data with a REST API, you would send a `POST` request to a specific endpoint and include information about the entities to be created in the body of the request. GraphQL takes a different approach.
+
+Let's look at an example mutation that's defined in our schema:
+
+```graphql
+enum Episode {
+ NEWHOPE
+ EMPIRE
+ JEDI
+}
+
+input ReviewInput {
+ stars: Int!
+ commentary: String
+}
+
+type Mutation {
+ createReview(episode: Episode, review: ReviewInput!): Review
+}
+```
+
+Like queries, mutation fields are added to one of the [root operation types](https://spec.graphql.org/draft/#sec-Root-Operation-Types) that provide an entry point to the API. In this case, we define the `createReview` field on the `Mutation` type.
+
+Mutation fields can also accept arguments and you might notice that the `review` argument has an input type set to `ReviewInput`. This is known as [Input Object type](/learn/schema/#input-object-types), which allows us to pass in a structured object containing information the mutation can use instead of individual scalar values only.
+
+Also like just like queries, if the mutation field returns an Object type, then you specify a selection set of its fields in the operation:
+
+```graphql
+# { "graphiql": true, "variables": { "ep": "JEDI", "review": { "stars": 5, "commentary": "This is a great movie!" } } }
+mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
+ createReview(episode: $ep, review: $review) {
+ stars
+ commentary
+ }
+}
+```
+
+While the `createReview` field could be defined with any valid output type in the schema, it's conventional to specify an output type that relates to whatever is modified during the mutation—in this case, the `Review` type. This can be useful for clients that need to fetch the new state of an object after an update.
+
+Recall that GraphQL is meant to work with your existing code and data, so the actual creation of the review is up to you when clients send this operation to the GraphQL server. A hypothetical function that writes the new review to a database during a `createReview` mutation might look like this:
+
+```js
+Mutation: {
+ createReview(obj, args, context, info) {
+ return context.db
+ .createNewReview(args.episode, args.review)
+ .then((reviewData) => new Review(reviewData))
+ }
+}
+```
+
+You can learn more about how GraphQL provides data for fields on the [Execution page](/learn/execution).
+
+## Update existing data
+
+Similarly, we use mutations to update existing data. To change a human's name, we'll define a new mutation field and set that field's output type to the `Human` type so we can return the updated human's information to client after the server successfully writes the data:
+
+```graphql
+type Mutation {
+ updateHumanName(id: ID!, name: String!): Human
+}
+```
+
+This operation will update Luke Skywalker's name:
+
+```graphql
+# { "graphiql": true, "variables": { "id": "1000", "name": "Luke Starkiller" } }
+mutation UpdateHumanName($id: ID!, $name: String!) {
+ updateHumanName(id: $id, name: $name ) {
+ id
+ name
+ }
+}
+```
+
+## Purpose-built mutations
+
+The previous example demonstrates an important distinction from REST. To update a human's properties using a REST API, you would likely send any updated data to a generalized endpoint for that resource using a `PATCH` request. With GraphQL, instead of simply creating an `updateHuman` mutation, you can define more specific mutation fields such as `updateHumanName`that are designed for the task at hand.
+
+Purpose-built mutation fields can help make a schema more expressive by allowing the input types for field arguments to be Non-Null types (a generic `updateHuman` mutation would likely need to accept many nullable arguments to handle different update scenarios). Defining this requirement in the schema also eliminates the need for other runtime logic to determine that the appropriate values were submitted to perform the client's desired write operation.
+
+GraphQL also allows us to express relationships between data that would be more difficult to model semantically with a basic CRUD-style request. For example, a user may wish to save a personal rating for a film. While the rating belongs to the user and doesn't modify anything related to a film itself, we can ergonomically associate it with a `Film` object as follows:
+
+```graphql
+# { "graphiql": true, "variables": { "episode": "EMPIRE", "rating": "THUMBS_UP" } }
+mutation RateFilm($episode: Episode!, $rating: FilmRating!) {
+ rateFilm(episode: $episode, rating: $rating) {
+ episode
+ viewerRating
+ }
+}
+```
+
+As a general rule, a GraphQL API should be designed to help clients get and modify data in a way that makes sense for them, so the fields defined in a schema should be informed by those use cases.
+
+## Remove existing data
+
+Just as we can send a `DELETE` request to delete a resource with a REST API, we can use mutations to delete some existing data as well by defining another field on the `Mutation` type:
+
+```graphql
+type Mutation {
+ deleteStarship(id: ID!): ID!
+}
+```
+
+Here's an example of the new mutation field:
+
+```graphql
+# { "graphiql": true, "variables": { "id": "3003" } }
+mutation DeleteStarship($id: ID!) {
+ deleteStarship(id: $id)
+}
+```
+
+As with mutations that create and update data, the GraphQL specification doesn't indicate what should be returned from a successful mutation operation that deletes data, but we do have to specify some type as an output type for the field in the schema. Commonly, the deleted entity's ID or a payload object containing data about the entity will be used to indicate that the operation was successful.
+
+## Multiple fields in mutations
+
+A mutation can contain multiple fields, just like a query. There's one important distinction between queries and mutations, other than the name:
+
+**While query fields are executed in parallel, mutation fields run in series.**
+
+Let's look at an example:
+
+```graphql
+# { "graphiql": true }
+mutation {
+ firstShip: deleteStarship(id: "3001")
+ secondShip: deleteStarship(id: "3002")
+}
+```
+
+[Serial execution](https://spec.graphql.org/draft/#sec-Normal-and-Serial-Execution) of these top-level fields means that if we send two `deleteStarship` mutations in one request, the first is guaranteed to finish before the second begins, ensuring that we don't end up in a race condition with ourselves.
+
+Note that serial execution of top-level `Mutation` fields differs from the notion of a database transaction. Some mutation fields may resolve successfully while others return errors, and there's no way for GraphQL to revert the successful portions of the operation when this happens. So in the previous example, if the first starship is removed successfully but the `secondShip` field raises an error, there is no built-in way for GraphQL to revert the execution of the `firstShip` field afterward.
+
+## Next steps
+
+To recap what we've learned about mutations:
+
+- Clients can create, update, and delete data using a GraphQL API, depending on what capabilities are exposed in the schema
+- Depending on client requirements, mutations can be designed to accommodate granular use cases for write operations
+- Top-level fields on the `Mutation` type will execute serially, unlike fields on other types which are often executed in parallel
+
+Now that we know how to use a GraphQL server to read and write data, we're ready to learn how to fetch data in real time using [subscriptions](/learn/subscriptions). You may also wish to learn more about how GraphQL queries and mutations can be [served over HTTP](/learn/serving-over-http/).
diff --git a/src/pages/learn/queries.mdx b/src/pages/learn/queries.mdx
index 8f2ff88b46..b34e103c07 100644
--- a/src/pages/learn/queries.mdx
+++ b/src/pages/learn/queries.mdx
@@ -1,10 +1,22 @@
-# Queries and Mutations
+import { Callout } from "nextra/components"
-On this page, you'll learn in detail about how to query a GraphQL server.
+# Queries
+
+Learn how to fetch data from a GraphQL server
+
+GraphQL supports three main operation types—queries, mutations, and subscriptions. We have already seen several examples of basic queries in this guide, and on this page, you'll learn in detail how to use the various features of query operations to read data from a server.
## Fields
-At its simplest, GraphQL is about asking for specific fields on objects. Let's start by looking at a very simple query and the result we get when we run it:
+At its simplest, GraphQL is about asking for specific [fields](https://spec.graphql.org/draft/#sec-Language.Fields) on objects. Let's start by looking at the `hero` field that's defined on the `Query` type in the schema:
+
+```graphql
+type Query {
+ hero: Character
+}
+```
+
+We can see what result we get when we query it:
```graphql
# { "graphiql": true }
@@ -15,20 +27,17 @@ At its simplest, GraphQL is about asking for specific fields on objects. Let's s
}
```
-You can see immediately that the query has exactly the same shape as the result. This is essential to GraphQL, because you always get back what you expect, and the server knows exactly what fields the client is asking for.
-
-The field `name` returns a `String` type, in this case the name of the main hero of Star Wars, `"R2-D2"`.
+When creating a GraphQL _document_ we always start with a [root operation type](/learn/schema/#the-query-mutation-and-subscription-types) (the `Query` Object type for this example) because it serves as an entry point to the API. From there we must specify the _selection set_ of fields we are interested in, all the way down to their leaf values which will be Scalar or Enum types. The field `name` returns a `String` type, in this case the name of the main hero of Star Wars, `"R2-D2"`.
-> Oh, one more thing - the query above is _interactive_. That means you can change it as you like and see the new result. Try adding an `appearsIn` field to the `hero` object in the query, and see the new result.
+The GraphQL specification indicates that a request's result will be returned on a top-level `data` key in the response. If the request raised any errors, there will be information about what went wrong on a top-level `errors` key. From there, you can see that the result has the same shape as the query. This is essential to GraphQL, because you always get back what you expect, and the server knows exactly what fields the client is asking for.
-In the previous example, we just asked for the name of our hero which returned a String, but fields can also refer to Objects. In that case, you can make a _sub-selection_ of fields for that object. GraphQL queries can traverse related objects and their fields, letting clients fetch lots of related data in one request, instead of making several roundtrips as one would need in a classic REST architecture.
+In the previous example, we just asked for the name of our hero which returned a `String`, but fields can also return Object types (and lists thereof). In that case, you can make a _sub-selection_ of fields for that Object type:
```graphql
# { "graphiql": true }
{
hero {
name
- # Queries can have comments!
friends {
name
}
@@ -36,144 +45,97 @@ In the previous example, we just asked for the name of our hero which returned a
}
```
-Note that in this example, the `friends` field returns an array of items. GraphQL queries look the same for both single items or lists of items; however, we know which one to expect based on what is indicated in the schema.
+GraphQL queries can traverse related objects and their fields, letting clients fetch lots of related data in one request, instead of making several roundtrips as one would need in a classic REST architecture.
+
+Note that in this example, the `friends` field returns an array of items. GraphQL queries look the same for single items or lists of items; however, we know which one to expect based on what is indicated in the schema.
## Arguments
-If the only thing we could do was traverse objects and their fields, GraphQL would already be a very useful language for data fetching. But when you add the ability to pass arguments to fields, things get much more interesting.
+If the only thing we could do was traverse objects and their fields, GraphQL would already be a very useful language for data fetching. But when you add the ability to pass [arguments](https://spec.graphql.org/draft/#sec-Language.Arguments) to fields, things get much more interesting:
```graphql
-# { "graphiql": true }
-{
- human(id: "1000") {
- name
- height
- }
+type Query {
+ droid(id: ID!): Droid
}
```
-In a system like REST, you can only pass a single set of arguments - the query parameters and URL segments in your request. But in GraphQL, every field and nested object can get its own set of arguments, making GraphQL a complete replacement for making multiple API fetches. You can even pass arguments into scalar fields, to implement data transformations once on the server, instead of on every client separately.
+The client must then provide the required `id` value with the query:
```graphql
# { "graphiql": true }
{
human(id: "1000") {
name
- height(unit: FOOT)
+ height
}
}
```
-Arguments can be of many different types. In the above example, we have used an Enumeration type, which represents one of a finite set of options (in this case, units of length, either `METER` or `FOOT`). GraphQL comes with a default set of types, but a GraphQL server can also declare its own custom types, as long as they can be serialized into your transport format.
+In a system like REST, you can only pass a single set of arguments—the query parameters and URL segments in your request. But in GraphQL, every field and nested object can get its own set of arguments, making GraphQL a complete replacement for making multiple API fetches.
-[Read more about the GraphQL type system here.](/learn/schema)
-
-## Aliases
-
-If you have a sharp eye, you may have noticed that, since the result object fields match the name of the field in the query but don't include arguments, you can't directly query for the same field with different arguments. That's why you need _aliases_ - they let you rename the result of a field to anything you want.
+You can even pass arguments into fields that output Scalar types; one use case for this would be to implement data transformations once on the server, instead of on every client separately:
```graphql
# { "graphiql": true }
{
- empireHero: hero(episode: EMPIRE) {
- name
- }
- jediHero: hero(episode: JEDI) {
+ human(id: "1000") {
name
+ height(unit: FOOT)
}
}
```
-In the above example, the two `hero` fields would have conflicted, but since we can alias them to different names, we can get both results in one request.
+Arguments can be of many different types. In the above example, we have used an Enum type, which represents one of a finite set of options (in this case, units of length, either `METER` or `FOOT`). GraphQL comes with a [default set of types](/learn/schema/#scalar-types), but a GraphQL server can also declare custom types, as long as they can be serialized into your transport format.
-## Fragments
+[Read more about the GraphQL type system here.](/learn/schema)
+
+## Operation type and name
-Let's say we had a relatively complicated page in our app, which lets us look at two heroes side by side, along with their friends. You can imagine that such a query could quickly get complicated, because we would need to repeat the fields at least once - one for each side of the comparison.
+In the examples above we have been using a shorthand syntax where we omit the `query` keyword before the operation's selection set. In addition to specifying the _operation type_ explicitly, we can also add a unique _operation name_, which is useful in production apps because it makes debugging and tracing easier.
-That's why GraphQL includes reusable units called _fragments_. Fragments let you construct sets of fields, and then include them in queries where you need to. Here's an example of how you could solve the above situation using fragments:
+Here’s an example that includes the `query` keyword as the operation type and `HeroNameAndFriends` as the operation name:
```graphql
# { "graphiql": true }
-{
- leftComparison: hero(episode: EMPIRE) {
- ...comparisonFields
- }
- rightComparison: hero(episode: JEDI) {
- ...comparisonFields
- }
-}
-
-fragment comparisonFields on Character {
- name
- appearsIn
- friends {
+query HeroNameAndFriends {
+ hero {
name
+ friends {
+ name
+ }
}
}
```
-You can see how the above query would be pretty repetitive if the fields were repeated. The concept of fragments is frequently used to split complicated application data requirements into smaller chunks, especially when you need to combine lots of UI components with different fragments into one initial data fetch.
-
-### Using variables inside fragments
-
-It is possible for fragments to access variables declared in the query or mutation. See [variables](#variables).
-
-```graphql
-# { "graphiql": true }
-query HeroComparison($first: Int = 3) {
- leftComparison: hero(episode: EMPIRE) {
- ...comparisonFields
- }
- rightComparison: hero(episode: JEDI) {
- ...comparisonFields
- }
-}
+The operation type is either `query`, `mutation`, or `subscription` and describes what type of operation you intend to do. This keyword is required unless you're using the shorthand syntax for queries (it is always required for mutations and subscriptions). Additionally, if you wish to provide a name for your operation, then you must specify the operation type as well.
-fragment comparisonFields on Character {
- name
- friendsConnection(first: $first) {
- totalCount
- edges {
- node {
- name
- }
- }
- }
-}
-```
+The operation name is an explicit name that you assign to your operation; you should pick a meaningful name. It is required when sending multiple operations in one document, but even if you're only sending one operation it's encouraged because operation names are helpful for debugging and server-side logging. When something goes wrong (you see errors either in your network logs or in the logs of your GraphQL server) it is easier to identify a query in your codebase by name instead of trying to decipher the contents.
-## Operation name
+Think of this just like a function name in your favorite programming language. For example, in JavaScript, we can easily work only with anonymous functions, but when we give a function a name, it's easier to track it down, debug our code, and log when it's called. In the same way, GraphQL query and mutation names, along with fragment names, can be a useful debugging tool on the server side to identify different GraphQL requests.
-In several of the examples above we have been using a shorthand syntax where we omit both the `query` keyword and the query name, but in production apps it's useful to use these to make our code less ambiguous.
+## Aliases
-Here’s an example that includes the keyword `query` as _operation type_ and `HeroNameAndFriends` as _operation name_ :
+If you have a sharp eye, you may have noticed that, since the result object fields match the name of the fields in the query but don't include arguments, you can't directly query for the same field with different arguments. That's why you need [aliases](https://spec.graphql.org/draft/#sec-Field-Alias)—they let you rename the result of a field to anything you want.
```graphql
# { "graphiql": true }
-query HeroNameAndFriends {
- hero {
+query {
+ empireHero: hero(episode: EMPIRE) {
+ name
+ }
+ jediHero: hero(episode: JEDI) {
name
- friends {
- name
- }
}
}
```
-The _operation type_ is either _query_, _mutation_, or _subscription_ and describes what type of operation you're intending to do. The operation type is required unless you're using the query shorthand syntax, in which case you can't supply a name or variable definitions for your operation.
-
-The _operation name_ is a meaningful and explicit name for your operation. It is only required in multi-operation documents, but its use is encouraged because it is very helpful for debugging and server-side logging.
-When something goes wrong (you see errors either in your network logs, or in the logs of your GraphQL server) it is easier to identify a query in your codebase by name instead of trying to decipher the contents.
-Think of this just like a function name in your favorite programming language.
-For example, in JavaScript we can easily work only with anonymous functions, but when we give a function a name, it's easier to track it down, debug our code,
-and log when it's called. In the same way, GraphQL query and mutation names, along with fragment names, can be a useful debugging tool on the server side to identify
-different GraphQL requests.
+In the above example, the two `hero` fields would have conflicted, but since we can alias them to different names, we can get both results in one request.
## Variables
-So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic: For example, there might be a dropdown that lets you select which Star Wars episode you are interested in, or a search field, or a set of filters.
+So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic. For example, there might be a dropdown that lets you select which Star Wars episode you are interested in, or a search field, or a set of filters.
-It wouldn't be a good idea to pass these dynamic arguments directly in the query string, because then our client-side code would need to dynamically manipulate the query string at runtime, and serialize it into a GraphQL-specific format. Instead, GraphQL has a first-class way to factor dynamic values out of the query, and pass them as a separate dictionary. These values are called _variables_.
+It wouldn't be a good idea to pass these dynamic arguments directly in the query string, because then our client-side code would need to dynamically manipulate the query string at runtime, and serialize it into a GraphQL-specific format. Instead, GraphQL has a first-class way to factor dynamic values out of the query and pass them as a separate dictionary. These values are called [variables](https://spec.graphql.org/draft/#sec-Language.Variables).
When we start working with variables, we need to do three things:
@@ -195,21 +157,25 @@ query HeroNameAndFriends($episode: Episode) {
}
```
-Now, in our client code, we can simply pass a different variable rather than needing to construct an entirely new query. This is also in general a good practice for denoting which arguments in our query are expected to be dynamic - we should never be doing string interpolation to construct queries from user-supplied values.
+
+You must specify an operation type and name in a GraphQL document to use variables.
+
+
+Now, in our client code, we can simply pass a different variable rather than needing to construct an entirely new query. In general, this is also a good practice for denoting which arguments in our query are expected to be dynamic—we should never be doing string interpolation to construct queries from user-supplied values.
### Variable definitions
-The variable definitions are the part that looks like `($episode: Episode)` in the query above. It works just like the argument definitions for a function in a typed language. It lists all of the variables, prefixed by `$`, followed by their type, in this case `Episode`.
+The variable definitions are the part that looks like `($episode: Episode)` in the query above. It works just like the argument definitions for a function in a typed language. It lists all of the variables, prefixed by `$`, followed by their type, in this case, `Episode`.
-All declared variables must be either scalars, enums, or input object types. So if you want to pass a complex object into a field, you need to know what input type that matches on the server. Learn more about input object types on the Schema page.
+All declared variables must be either Scalar, Enum, or Input Object types. So if you want to pass a complex object into a field, you need to know what [input type](/learn/schema/#input-object-types) matches it on the server.
Variable definitions can be optional or required. In the case above, since there isn't an `!` next to the `Episode` type, it's optional. But if the field you are passing the variable into requires a non-null argument, then the variable has to be required as well.
-To learn more about the syntax for these variable definitions, it's useful to learn [the GraphQL schema language](/learn/schema). The schema language is explained in detail on [the Schemas and Types page](/learn/schema).
+To learn more about the syntax for these variable definitions, it's useful to learn schema definition language (SDL), which is explained in detail on [the Schemas and Types page](/learn/schema).
### Default variables
-Default values can also be assigned to the variables in the query by adding the default value after the type declaration.
+Default values can also be assigned to the variables in the query by adding the default value after the type declaration:
```graphql
query HeroNameAndFriends($episode: Episode = JEDI) {
@@ -224,68 +190,67 @@ query HeroNameAndFriends($episode: Episode = JEDI) {
When default values are provided for all variables, you can call the query without passing any variables. If any variables are passed as part of the variables dictionary, they will override the defaults.
-## Directives
+## Fragments
-We discussed above how variables enable us to avoid doing manual string interpolation to construct dynamic queries. Passing variables in arguments solves a pretty big class of these problems, but we might also need a way to dynamically change the structure and shape of our queries using variables. For example, we can imagine a UI component that has a summarized and detailed view, where one includes more fields than the other.
+Let's say we have a relatively complicated page in our app, which lets us look at two heroes side by side, along with their friends. You can imagine that such a query could quickly get complicated because we would need to repeat the fields at least once—one for each side of the comparison.
-Let's construct a query for such a component:
+That's why GraphQL includes reusable units called [fragments](https://spec.graphql.org/draft/#sec-Language.Fragments). Fragments let you construct sets of fields, and then include them in queries where needed. Here's an example of how you could solve the above situation using fragments:
```graphql
-# { "graphiql": true, "variables": { "episode": "JEDI", "withFriends": false } }
-query Hero($episode: Episode, $withFriends: Boolean!) {
- hero(episode: $episode) {
+# { "graphiql": true }
+query {
+ leftComparison: hero(episode: EMPIRE) {
+ ...comparisonFields
+ }
+ rightComparison: hero(episode: JEDI) {
+ ...comparisonFields
+ }
+}
+
+fragment comparisonFields on Character {
+ name
+ appearsIn
+ friends {
name
- friends @include(if: $withFriends) {
- name
- }
}
}
```
-Try editing the variables above to instead pass `true` for `withFriends`, and see how the result changes.
-
-We needed to use a new feature in GraphQL called a _directive_. A directive can be attached to a field or fragment inclusion, and can affect execution of the query in any way the server desires. The core GraphQL specification includes exactly two directives, which must be supported by any spec-compliant GraphQL server implementation:
-
-- `@include(if: Boolean)` Only include this field in the result if the argument is `true`.
-- `@skip(if: Boolean)` Skip this field if the argument is `true`.
+You can see how the above query would be pretty repetitive if we weren't able to use fragments. The concept of fragments is frequently used to split complicated application data requirements into smaller chunks, especially when you need to combine many UI components with different fragments into one initial data fetch.
-Directives can be useful to get out of situations where you otherwise would need to do string manipulation to add and remove fields in your query. Server implementations may also add experimental features by defining completely new directives.
-
-## Mutations
-
-Most discussions of GraphQL focus on data fetching, but any complete data platform needs a way to modify server-side data as well.
-
-In REST, any request might end up causing some side-effects on the server, but by convention it's suggested that one doesn't use `GET` requests to modify data. GraphQL is similar - technically any query could be implemented to cause a data write. However, it's useful to establish a convention that any operations that cause writes should be sent explicitly via a mutation.
+### Using variables inside fragments
-Just like in queries, if the mutation field returns an object type, you can ask for nested fields. This can be useful for fetching the new state of an object after an update. Let's look at a simple example mutation:
+It is possible for fragments to access variables declared in the operation as well:
```graphql
-# { "graphiql": true, "variables": { "ep": "JEDI", "review": { "stars": 5, "commentary": "This is a great movie!" } } }
-mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
- createReview(episode: $ep, review: $review) {
- stars
- commentary
+# { "graphiql": true }
+query HeroComparison($first: Int = 3) {
+ leftComparison: hero(episode: EMPIRE) {
+ ...comparisonFields
+ }
+ rightComparison: hero(episode: JEDI) {
+ ...comparisonFields
}
}
-```
-
-Note how `createReview` field returns the `stars` and `commentary` fields of the newly created review. This is especially useful when mutating existing data, for example, when incrementing a field, since we can mutate and query the new value of the field with one request.
-
-You might also notice that, in this example, the `review` variable we passed in is not a scalar. It's an _input object type_, a special kind of object type that can be passed in as an argument. Learn more about input types on the Schema page.
-
-### Multiple fields in mutations
-
-A mutation can contain multiple fields, just like a query. There's one important distinction between queries and mutations, other than the name:
-**While query fields are executed in parallel, mutation fields run in series, one after the other.**
-
-This means that if we send two `incrementCredits` mutations in one request, the first is guaranteed to finish before the second begins, ensuring that we don't end up with a race condition with ourselves.
+fragment comparisonFields on Character {
+ name
+ friendsConnection(first: $first) {
+ totalCount
+ edges {
+ node {
+ name
+ }
+ }
+ }
+}
+```
-## Inline Fragments
+### Inline Fragments
-Like many other type systems, GraphQL schemas include the ability to define interfaces and union types. [Learn about them in the schema guide.](/learn/schema/#interfaces)
+Like many other type systems, GraphQL schemas include the ability to define Interface and Union types. You can learn more about them on the [Schemas and Types page.](/learn/schema/#interfaces)
-If you are querying a field that returns an interface or a union type, you will need to use _inline fragments_ to access data on the underlying concrete type. It's easiest to see with an example:
+If you are querying a field that returns an Interface or a Union type, you will need to use _inline fragments_ to access data on the underlying concrete type. It's easiest to see with an example:
```graphql
# { "graphiql": true, "variables": { "ep": "JEDI" } }
@@ -302,15 +267,17 @@ query HeroForEpisode($ep: Episode!) {
}
```
-In this query, the `hero` field returns the type `Character`, which might be either a `Human` or a `Droid` depending on the `episode` argument. In the direct selection, you can only ask for fields that exist on the `Character` interface, such as `name`.
+In this query, the `hero` field returns the type `Character`, which might be either a `Human` or a `Droid` depending on the `episode` argument. In the direct selection, you can only ask for fields on the `Character` interface, such as `name`.
-To ask for a field on the concrete type, you need to use an _inline fragment_ with a type condition. Because the first fragment is labeled as `... on Droid`, the `primaryFunction` field will only be executed if the `Character` returned from `hero` is of the `Droid` type. Similarly for the `height` field for the `Human` type.
+To ask for a field on the concrete type, you need to use an inline fragment with a type condition. Because the first fragment is labeled as `... on Droid`, the `primaryFunction` field will only be executed if the `Character` returned from `hero` is of the `Droid` type. Similarly for the `height` field for the `Human` type.
Named fragments can also be used in the same way, since a named fragment always has a type attached.
### Meta fields
-Given that there are some situations where you don't know what type you'll get back from the GraphQL service, you need some way to determine how to handle that data on the client. GraphQL allows you to request `__typename`, a meta field, at any point in a query to get the name of the object type at that point.
+As we have seen with [Union types](/learn/schema/#union-types), there are some situations where you don't know what type you'll get back from the GraphQL service so you need some way to determine how to handle that data on the client.
+
+GraphQL allows you to request `__typename`, a meta field, at any point in a query to get the name of the Object type at that point:
```graphql
# { "graphiql": true}
@@ -330,6 +297,54 @@ Given that there are some situations where you don't know what type you'll get b
}
```
-In the above query, `search` returns a union type that can be one of three options. It would be impossible to tell apart the different types from the client without the `__typename` field.
+In the above query, `search` returns a Union type that can be one of three options. Without the `__typename` field, it would be impossible for a client to tell the different types apart.
+
+All field names beginning with two underscores (`__`) are reserved by GraphQL. In addition to `__typename`, GraphQL services provide the `__schema` and `__type` meta-fields which expose the [introspection](/learn/introspection) system.
+
+## Directives
+
+We discussed above how variables enable us to avoid doing manual string interpolation to construct dynamic queries. Passing variables in arguments solves a large class of these problems, but we might also need a way to dynamically change the structure and shape of our queries using variables. For example, we can imagine a UI component that has a summarized and detailed view, where one includes more fields than the other.
+
+Let's construct a query for such a component:
+
+```graphql
+# { "graphiql": true, "variables": { "episode": "JEDI", "withFriends": false } }
+query Hero($episode: Episode, $withFriends: Boolean!) {
+ hero(episode: $episode) {
+ name
+ friends @include(if: $withFriends) {
+ name
+ }
+ }
+}
+```
+
+Try editing the variables above to instead pass `true` for `withFriends`, and see how the result changes.
+
+We needed to use a feature in GraphQL called a [directive](https://spec.graphql.org/draft/#sec-Type-System.Directives). Specifically, an _executable directive_ can be attached to a field or fragment inclusion by a client, and can affect execution of the query in any way the server desires. The core GraphQL specification includes exactly two directives, which must be supported by any spec-compliant GraphQL server implementation:
+
+- `@include(if: Boolean)` Only include this field in the result if the argument is `true`.
+- `@skip(if: Boolean)` Skip this field if the argument is `true`.
+
+Directives can be useful to get out of situations where you otherwise would need to do string manipulation to add and remove fields in your query. Server implementations may also add experimental features by defining completely new directives.
+
+
+Looking for information on how to define directives that can be used to annotate the types, fields, or arguments in your GraphQL schema? See the [Schemas and Types page](/learn/schema/#directives) for more information on defining and using type system directives.
+
+
+## Next steps
+
+To recap what we've learned about queries:
+
+- A GraphQL operation that reads data starts at the `query` root operation type and traverses the fields in the selection set down to the leaf values, which will be Scalar or Enum types
+- Fields can accept arguments that alter the output of that field
+- Operations can use the `query`, `mutation`, or `subscription` keyword to indicate their type
+- The operation type keyword can be omitted for certain query operations only
+- Operations should be given unique names, which make requests more expressive and help with debugging
+- Field aliases allow you to rename response keys, include the same field multiple times in the same query, and provide different arguments to the aliased fields
+- Variables are preceded by the `$` character and can be used to provide dynamic values to field arguments
+- A fragment is a reusable selection set of fields that can be used as needed in multiple queries
+- Executable directive can be applied to queries to change the result of a GraphQL query when it's executed on the server
+- All spec-compliant GraphQL servers include the `@include` and `@skip` built-in directives
-GraphQL services provide a few meta fields, the rest of which are used to expose the [Introspection](/learn/introspection) system.
+Now that we understand the ins and outs of how to read data from a GraphQL server with query operations, it's time to learn how to change data and trigger side effects using [mutations](/learn/mutations/).
diff --git a/src/pages/learn/subscriptions.mdx b/src/pages/learn/subscriptions.mdx
new file mode 100644
index 0000000000..cabc6cf180
--- /dev/null
+++ b/src/pages/learn/subscriptions.mdx
@@ -0,0 +1,102 @@
+# Subscriptions
+
+import { Callout } from "nextra/components"
+
+Learn how to get real-time updates from a GraphQL server
+
+In addition to reading and writing data using stateless query and mutation operations, the GraphQL specification also describes how to receive real-time updates via long-lived requests. On this page, we'll explore how clients can subscribe to details of events on a GraphQL server using subscription operations.
+
+
+Many of the features of GraphQL operations that apply to queries also apply to subscriptions, so review the [Queries](/learn/queries/) page first before proceeding.
+
+
+## Subscribing to updates
+
+Subscription fields are defined exactly as `Query` and `Mutation` fields are, but using the `subscription` root operation type instead:
+
+```graphql
+type Subscription {
+ reviewCreated: Review
+}
+```
+
+Similarly, a subscription is initiated using the `subscription` keyword as the operation type:
+
+```graphql
+subscription NewReviewCreated {
+ reviewCreated {
+ rating
+ commentary
+ }
+}
+```
+
+GraphQL subscriptions are typically backed by a separate pub/sub system so that messages about updated data can be published as needed during runtime and then consumed by the resolver functions for the subscription fields in the API.
+
+For the previous example, we could imagine publishing a `REVIEW_CREATED` message to a pub/sub system when a client submits a new review with the `createReview` mutation, and then listening for this event in the resolver function for the `reviewCreated` subscription field so that the server may send this data to subscribed clients.
+
+
+Note that subscriptions are different from the concept of _live queries_, a loosely defined feature that some GraphQL implementations may offer. [Read more about the ongoing discussion on a formal specification for live queries here.](https://github.com/graphql/graphql-spec/issues/386)
+
+
+As with query and mutation operations, GraphQL doesn't specify what transport protocol to use, so it's up to the server to decide. In practice, you will often see them implemented with WebSockets or server-sent events. Clients that want to send subscription operations will also need to support the chosen protocol. There are community-maintained specifications for implementing GraphQL subscriptions with [WebSockets](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) and [server-sent events](https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md).
+
+One important distinction to make with subscription operations is that a document may contain a number of different subscription operations with different root fields, but each operation must have exactly one root field.
+
+For example, this operation would be invalid:
+
+```graphql
+subscription {
+ reviewCreated {
+ rating
+ commentary
+ }
+ humanFriendsUpdated {
+ name
+ friends {
+ name
+ }
+ }
+}
+```
+
+But this document is valid:
+
+```graphql
+subscription NewReviewCreated {
+ reviewCreated {
+ rating
+ commentary
+ }
+}
+subscription FriendListUpdated($id: ID!) {
+ humanFriendsUpdated(id: $id) {
+ name
+ friends {
+ name
+ }
+ }
+}
+```
+
+As with query and mutation operations, the document above contains multiple operations so each operation must be named, and the name of the operation to execute must be specified in the request.
+
+## Using subscriptions at scale
+
+Subscription operations are a powerful feature of GraphQL, but they are more complicated to implement than queries or mutations because you must maintain the GraphQL document, variables, and other context over the lifetime of the subscription. These requirements can be challenging when scaling GraphQL servers horizontally because each subscribed client must be bound to a specific instance of the server.
+
+Similarly, client libraries will typically need more advanced features to handle these kinds of operations, such as logic to resubscribe to operations if the connection is disrupted or mechanisms to handle potential race conditions between initial queries and updated subscription data.
+
+In practice, a GraphQL API that supports subscriptions will call for a more complicated architecture than one that only exposes query and mutation fields. How this architecture is designed will depend on the specific GraphQL implementation, what pub/sub system supports the subscriptions, and the chosen transport protocol, as well as other requirements related to availability, scalability, and security of the real-time data.
+
+Subscription operations are well suited for data that changes often and incrementally, and for clients that need to receive those incremental updates as close to real-time as possible to deliver the expected user experience. For data with less frequent updates, periodic polling, mobile push notifications, or re-fetching queries based on user interaction may be the better solutions for keeping a client UI up to date.
+
+## Next steps
+
+To recap what we've learned about subscriptions:
+
+- GraphQL subscription operations can be used for incremental data updates via long-lived requests
+- Subscription operations are typically supported by a stateful transport protocol, so they will require more effort to implement and support than queries and mutations
+- Data-fetching use cases that require frequent and incremental updates to be streamed to a client are best suited for subscriptions
+
+Now that we've covered all three operation types in GraphQL, let's explore how client requests are [validated](/learn/validation/) by a GraphQL server.