diff --git a/src/components/marked/swapi-schema.tsx b/src/components/marked/swapi-schema.tsx index f1b5dcdbe2..0f56f9be3e 100644 --- a/src/components/marked/swapi-schema.tsx +++ b/src/components/marked/swapi-schema.tsx @@ -17,7 +17,7 @@ const typeDefs = /* GraphQL */ ` mutation: Mutation } - # The query type, represents all of the entry points into our object graph + "The query type, represents all of the entry points into our object graph" type Query { hero(episode: Episode): Character reviews(episode: Episode!): [Review] @@ -28,155 +28,155 @@ const typeDefs = /* GraphQL */ ` starship(id: ID!): Starship } - # The mutation type, represents all updates we can make to our data + "The mutation type, represents all updates we can make to our data" type Mutation { createReview(episode: Episode, review: ReviewInput!): Review } - # The episodes in the Star Wars trilogy + "The episodes in the Star Wars trilogy" enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. + "Star Wars Episode IV: A New Hope, released in 1977." NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. + "Star Wars Episode V: The Empire Strikes Back, released in 1980." EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. + "Star Wars Episode VI: Return of the Jedi, released in 1983." JEDI } - # A character from the Star Wars universe + "A character from the Star Wars universe" interface Character { - # The ID of the character + "The ID of the character" id: ID! - # The name of the character + "The name of the character" name: String! - # The friends of the character, or an empty list if they have none + "The friends of the character, or an empty list if they have none" friends: [Character] - # The friends of the character exposed as a connection with edges + "The friends of the character exposed as a connection with edges" friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in + "The movies this character appears in" appearsIn: [Episode]! } - # Units of height + "Units of height" enum LengthUnit { - # The standard unit around the world + "The standard unit around the world" METER - # Primarily used in the United States + "Primarily used in the United States" FOOT } - # A humanoid creature from the Star Wars universe + "A humanoid creature from the Star Wars universe" type Human implements Character { - # The ID of the human + "The ID of the human" id: ID! - # What this human calls themselves + "What this human calls themselves" name: String! - # Height in the preferred unit, default is meters + "Height in the preferred unit, default is meters" height(unit: LengthUnit = METER): Float - # Mass in kilograms, or null if unknown + "Mass in kilograms, or null if unknown" mass: Float - # This human's friends, or an empty list if they have none + "This human's friends, or an empty list if they have none" friends: [Character] - # The friends of the human exposed as a connection with edges + "The friends of the human exposed as a connection with edges" friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in + "The movies this human appears in" appearsIn: [Episode]! - # A list of starships this person has piloted, or an empty list if none + "A list of starships this person has piloted, or an empty list if none" starships: [Starship] } - # An autonomous mechanical character in the Star Wars universe + "An autonomous mechanical character in the Star Wars universe" type Droid implements Character { - # The ID of the droid + "The ID of the droid" id: ID! - # What others call this droid + "What others call this droid" name: String! - # This droid's friends, or an empty list if they have none + "This droid's friends, or an empty list if they have none" friends: [Character] - # The friends of the droid exposed as a connection with edges + "The friends of the droid exposed as a connection with edges" friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in + "The movies this droid appears in" appearsIn: [Episode]! - # This droid's primary function + "This droid's primary function" primaryFunction: String } - # A connection object for a character's friends + "A connection object for a character's friends" type FriendsConnection { - # The total number of friends + "The total number of friends" totalCount: Int - # The edges for each of the character's friends. + "The edges for each of the character's friends." edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. + "A list of the friends, as a convenience when edges are not needed." friends: [Character] - # Information for paginating this connection + "Information for paginating this connection" pageInfo: PageInfo! } - # An edge object for a character's friends + "An edge object for a character's friends" type FriendsEdge { - # A cursor used for pagination + "A cursor used for pagination" cursor: ID! - # The character represented by this friendship edge + "The character represented by this friendship edge" node: Character } - # Information for paginating this connection + "Information for paginating this connection" type PageInfo { startCursor: ID endCursor: ID hasNextPage: Boolean! } - # Represents a review for a movie + "Represents a review for a movie" type Review { - # The number of stars this review gave, 1-5 + "The number of stars this review gave, 1-5" stars: Int! - # Comment about the movie + "Comment about the movie" commentary: String } - # The input object sent when someone is creating a new review + "The input object sent when someone is creating a new review" input ReviewInput { - # 0-5 stars + "0-5 stars" stars: Int! - # Comment about the movie, optional + "Comment about the movie, optional" commentary: String } type Starship { - # The ID of the starship + "The ID of the starship" id: ID! - # The name of the starship + "The name of the starship" name: String! - # Length of the starship, along the longest axis + "Length of the starship, along the longest axis" length(unit: LengthUnit = METER): Float } diff --git a/src/pages/learn/introspection.mdx b/src/pages/learn/introspection.mdx index a4cfa7462b..f7b36e5f4b 100644 --- a/src/pages/learn/introspection.mdx +++ b/src/pages/learn/introspection.mdx @@ -1,23 +1,41 @@ # Introspection -It's often useful to ask a GraphQL schema for information about what -queries it supports. GraphQL allows us to do so using the introspection -system! +
Learn how to query information about a GraphQL schema
-For our Star Wars example, the file -[starWarsIntrospection-test.ts](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsIntrospection-test.ts) -contains a number of queries demonstrating the introspection system, and is a -test file that can be run to exercise the reference implementation's -introspection system. +It's often useful to ask a GraphQL schema for information about what features it supports. GraphQL allows us to do so using the [introspection system](https://spec.graphql.org/draft/#sec-Introspection). -We designed the type system, so we know what types are available, but if -we didn't, we can ask GraphQL, by querying the `__schema` field, always -available on the root type of a Query. Let's do so now, and ask what types -are available. +Introspection queries are special kinds of queries that allow you to learn about a GraphQL API's schema, and they also help power GraphQL development tools. On this page, we'll learn how to run different queries to learn more about an underlying schema's types, fields, and descriptions. + +## Type name introspection + +We have already seen an example of introspection on the [Schemas and Types page](/learn/schema/). When querying a field that returned Union type, we included the `__typename` meta-field directly in a selection set to get the string value of the names of the different types returned by a search query. Let's look at this example again: + +```graphql +# { "graphiql": true } +query { + search(text: "an") { + __typename + ... on Character { + name + } + ... on Starship { + name + } + } +} +``` + +We didn't add the `__typename` field to our GraphQL API explicitly—the GraphQL specification says that it must be provided to clients by a GraphQL implementation. This field can be queried for any field with an Object, Interface, or Union type as the underlying output type. + +## Schema introspection + +Introspection can do more than provide type names in a query. If you designed the type system for a GraphQL API, then you'll likely already know what types are available. But if you didn't design it, you can ask GraphQL by querying the `__schema` field, which is always available on the `query` root operation type. + +Let's do so now and ask what types are available in the Star Wars schema: ```graphql # { "graphiql": true } -{ +query { __schema { types { name @@ -28,21 +46,15 @@ are available. Wow, that's a lot of types! What are they? Let's group them: -- `Query`, `Character`, `Human`, `Episode`, `Droid` - These are the ones that we - defined in our type system. -- `String`, `Boolean` - These are built-in scalars that the type system - provided. -- `__Schema`, `__Type`, `__TypeKind`, `__Field`, `__InputValue`, - `__EnumValue`, `__Directive` - These all are preceded with a double - underscore, indicating that they are part of the introspection system. +- Types that we defined in our type system: `Query`, `Mutation`, `Character`, `Human`, `Episode`, `Droid`, `LengthUnit`, `FriendsConnection`, `FriendsEdge`, `PageInfo`, `Review`, `ReviewInput`, `Starship`, and `SearchResult` +- Built-in scalars that the type system provided: `Boolean`, `Float`, `ID`, `Int`, and `String` +- Types preceded with a double underscore that are part of the introspection system: `__Schema`, `__Type`, `__TypeKind`, `__Field`, `__InputValue`, `__EnumValue`, `__Directive`, and `__DirectiveLocation` -Now, let's try and figure out a good place to start exploring what queries are -available. When we designed our type system, we specified what type all queries -would start at; let's ask the introspection system about that! +Now, let's try to figure out a good place to start exploring what queries are available. When we designed our type system, we specified what type all queries would start at; let's ask the introspection system about that: ```graphql # { "graphiql": true } -{ +query { __schema { queryType { name @@ -51,31 +63,24 @@ would start at; let's ask the introspection system about that! } ``` -And that matches what we said in the type system section, that -the `Query` type is where we will start! Note that the naming here -was just by convention; we could have named our `Query` type anything -else, and it still would have been returned here had we specified it -was the starting type for queries. Naming it `Query`, though, is a useful -convention. +The result matches what we said in the [type system section](/learn/schema/#type-system)—that the `Query` type is where we will start. Note that the naming here was just by convention; we could have named our `Query` type anything else, and it still would have been returned here had we specified it was the starting type for queries. Naming it `Query`, though, is a useful convention. -It is often useful to examine one specific type. Let's take a look at -the `Droid` type: +It is often useful to examine one specific type. Let's take a look at the `Droid` type: ```graphql # { "graphiql": true } -{ +query { __type(name: "Droid") { name } } ``` -What if we want to know more about Droid, though? For example, is it -an interface or an object? +But what if we want to know more about Droid? For example, is it an Interface or Object type? ```graphql # { "graphiql": true } -{ +query { __type(name: "Droid") { name kind @@ -83,12 +88,11 @@ an interface or an object? } ``` -`kind` returns a `__TypeKind` enum, one of whose values is `OBJECT`. If -we asked about `Character` instead we'd find that it is an interface: +`kind` returns a `__TypeKind` Enum type, one of whose values is `OBJECT`. If we asked about `Character` instead we'd find that it is an Interface type: ```graphql # { "graphiql": true } -{ +query { __type(name: "Character") { name kind @@ -96,12 +100,11 @@ we asked about `Character` instead we'd find that it is an interface: } ``` -It's useful for an object to know what fields are available, so let's -ask the introspection system about `Droid`: +It's useful for an Object type to know what fields are available, so let's ask the introspection system about `Droid`: ```graphql # { "graphiql": true } -{ +query { __type(name: "Droid") { name fields { @@ -115,20 +118,15 @@ ask the introspection system about `Droid`: } ``` -Those are our fields that we defined on `Droid`! +Those are the fields that we defined on `Droid`! -`id` looks a bit weird there, it has no name for the type. That's -because it's a "wrapper" type of kind `NON_NULL`. If we queried for -`ofType` on that field's type, we would find the `ID` type there, -telling us that this is a non-null ID. +`id` looks a bit weird there, it has no name for the type. That's because it's a _wrapper type_ of kind `NON_NULL`. If we queried for `ofType` on that field's type, we would find the `ID` type there, telling us this is a non-null ID. -Similarly, both `friends` and `appearsIn` have no name, since they are the -`LIST` wrapper type. We can query for `ofType` on those types, which will -tell us what these are lists of. +Similarly, both `friends` and `appearsIn` have no name, since they are the `LIST` wrapper type. We can query for `ofType` on those types, which will tell us what types are inside the list: ```graphql # { "graphiql": true } -{ +query { __type(name: "Droid") { name fields { @@ -146,12 +144,11 @@ tell us what these are lists of. } ``` -Let's end with a feature of the introspection system particularly useful -for tooling; let's ask the system for documentation! +Let's end with a feature of the introspection system particularly useful for tooling; let's ask the system for documentation: ```graphql # { "graphiql": true } -{ +query { __type(name: "Droid") { name description @@ -159,12 +156,24 @@ for tooling; let's ask the system for documentation! } ``` -So we can access the documentation about the type system using introspection, -and create documentation browsers, or rich IDE experiences. +As demonstrated above, we can access the documentation about the type system using introspection and create documentation browsers or rich IDE experiences. + +This has just scratched the surface of the introspection system; we can query for Enum type values, what Interface types another type implements, and more. We can even introspect on the introspection system itself. + +To see an example of a specification-compliant GraphQL query introspection system implemented in code, you can view [src/type/introspection.ts](https://github.com/graphql/graphql-js/blob/e9b6b626f6f6aa379bb8f8c48df40d0c02a26082/src/type/introspection.ts) in the reference implementation. + +## Introspection in production + +Introspection is a useful feature of GraphQL, especially for client developers and tooling. However, for APIs intended only for your own applications, it’s typically not needed in production—required operations are usually baked into these applications at build time, making runtime introspection unnecessary. + +Disabling introspection in production is common in order to reduce the API’s attack surface. This is often part of a broader API security strategy, which may also include authentication and authorization, operation safe-listing (or a range of alternative protections, such as depth-limiting, breadth-limiting, alias limits, cycle rejection, cost analysis, etc.), execution timeouts, and more. + +## Next steps + +To recap what we've learned about introspection: + +- Type names can be queried in a field selection set for an Object, Interface, or Union type using the `__typename` meta-field +- Information about the elements of a GraphQL schema can be queried using the `__schema` field on the `query` root operation type +- Introspection is often disabled in production environments -This has just scratched the surface of the introspection system; we can -query for enum values, what interfaces a type implements, and more. We -can even introspect on the introspection system itself. The specification goes -into more detail about this topic in the "Introspection" section, and the [introspection](https://github.com/graphql/graphql-js/blob/main/src/type/introspection.ts) -file in GraphQL.js contains code implementing a specification-compliant GraphQL -query introspection system. +Now that you've explored the GraphQL type system, how to query data from an API, and what the lifecycle of a request looks like, head over to the [Best Practices](/learn/best-practices/) section to learn more about running GraphQL in production. \ No newline at end of file