在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):graphql/graphql-spec开源软件地址(OpenSource Url):https://github.com/graphql/graphql-spec开源编程语言(OpenSource Language):Shell 100.0%开源软件介绍(OpenSource Introduction):GraphQLThe GraphQL specification is edited in the markdown files found in
The latest draft specification can be found at https://graphql.github.io/graphql-spec/draft/ which tracks the latest commit to the main branch in this repository. Previous releases of the GraphQL specification can be found at permalinks that match their release tag. For example, https://graphql.github.io/graphql-spec/October2016/. If you are linking directly to the GraphQL specification, it's best to link to a tagged permalink for the particular referenced version. OverviewThis is a Working Draft of the Specification for GraphQL, a query language for APIs created by Facebook. The target audience for this specification is not the client developer, but those who have, or are actively interested in, building their own GraphQL implementations and tools. In order to be broadly adopted, GraphQL will have to target a wide variety of backend environments, frameworks, and languages, which will necessitate a collaborative effort across projects and organizations. This specification serves as a point of coordination for this effort. Looking for help? Find resources from the community. Getting StartedGraphQL consists of a type system, query language and execution semantics, static validation, and type introspection, each outlined below. To guide you through each of these components, we've written an example designed to illustrate the various pieces of GraphQL. This example is not comprehensive, but it is designed to quickly introduce the core concepts of GraphQL, to provide some context before diving into the more detailed specification or the GraphQL.js reference implementation. The premise of the example is that we want to use GraphQL to query for information about characters and locations in the original Star Wars trilogy. Type SystemAt the heart of any GraphQL implementation is a description of what types of objects it can return, described in a GraphQL type system and returned in the GraphQL Schema. For our Star Wars example, the starWarsSchema.ts file in GraphQL.js defines this type system. The most basic type in the system will be type Human {
name: String
} This shorthand is convenient for describing the basic shape of a type system; the JavaScript implementation is more full-featured, and allows types and fields to be documented. It also sets up the mapping between the type system and the underlying data; for a test case in GraphQL.js, the underlying data is a set of JavaScript objects, but in most cases the backing data will be accessed through some service, and this type system layer will be responsible for mapping from types and fields to that service. A common pattern in many APIs, and indeed in GraphQL is to give objects an ID that can be used to refetch the object. So let's add that to our Human type. We'll also add a string for their home planet. type Human {
id: String
name: String
homePlanet: String
} Since we're talking about the Star Wars trilogy, it would be useful to describe the episodes in which each character appears. To do so, we'll first define an enum, which lists the three episodes in the trilogy: enum Episode {
NEWHOPE
EMPIRE
JEDI
} Now we want to add a field to type Human {
id: String
name: String
appearsIn: [Episode]
homePlanet: String
} Now, let's introduce another type, type Droid {
id: String
name: String
appearsIn: [Episode]
primaryFunction: String
} Now we have two types! Let's add a way of going between them: humans and droids both have friends. But humans can be friends with both humans and droids. How do we refer to either a human or a droid? If we look, we note that there's common functionality between humans and droids;
they both have IDs, names, and episodes in which they appear. So we'll add an
interface, Our type system so far is: enum Episode {
NEWHOPE
EMPIRE
JEDI
}
interface Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
primaryFunction: String
} One question we might ask, though, is whether any of those fields can return
Note that while in our current implementation, we can guarantee that more fields are non-null (since our current implementation has hard-coded data), we didn't mark them as non-null. One can imagine we would eventually replace our hardcoded data with a backend service, which might not be perfectly reliable; by leaving these fields as nullable, we allow ourselves the flexibility to eventually return null to indicate a backend error, while also telling the client that the error occurred. enum Episode {
NEWHOPE
EMPIRE
JEDI
}
interface Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
primaryFunction: String
} We're missing one last piece: an entry point into the type system. When we define a schema, we define an object type that is the basis for all
query operations. The name of this type is type Query {
hero(episode: Episode): Character
human(id: String!): Human
droid(id: String!): Droid
} In this example, there are three top-level operations that can be done on our schema:
These fields demonstrate another feature of the type system, the ability for a field to specify arguments that configure their behavior. When we package the whole type system together, defining the This example just scratched the surface of the type system. The specification goes into more detail about this topic in the "Type System" section, and the type directory in GraphQL.js contains code implementing a specification-compliant GraphQL type system. Query SyntaxGraphQL queries declaratively describe what data the issuer wishes to fetch from whoever is fulfilling the GraphQL query. For our Star Wars example, the starWarsQueryTests.js file in the GraphQL.js repository contains a number of queries and responses. That file is a test file that uses the schema discussed above and a set of sample data, located in starWarsData.js. This test file can be run to exercise the reference implementation. An example query on the above schema would be: query HeroNameQuery {
hero {
name
}
} The initial line, {
"hero": {
"name": "R2-D2"
}
} Specifying the {
hero {
name
}
} Assuming that the backing data for the GraphQL server identified R2-D2 as the hero. The response continues to vary based on the request; if we asked for R2-D2's ID and friends with this query: query HeroNameAndFriendsQuery {
hero {
id
name
friends {
id
name
}
}
} then we'll get back a response like this: {
"hero": {
"id": "2001",
"name": "R2-D2",
"friends": [
{
"id": "1000",
"name": "Luke Skywalker"
},
{
"id": "1002",
"name": "Han Solo"
},
{
"id": "1003",
"name": "Leia Organa"
}
]
}
} One of the key aspects of GraphQL is its ability to nest queries. In the above query, we asked for R2-D2's friends, but we can ask for more information about each of those objects. So let's construct a query that asks for R2-D2's friends, gets their name and episode appearances, then asks for each of their friends. query NestedQuery {
hero {
name
friends {
name
appearsIn
friends {
name
}
}
}
} which will give us the nested response {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{ "name": "Han Solo" },
{ "name": "Leia Organa" },
{ "name": "C-3PO" },
{ "name": "R2-D2" }
]
},
{
"name": "Han Solo",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{ "name": "Luke Skywalker" },
{ "name": "Leia Organa" },
{ "name": "R2-D2" }
]
},
{
"name": "Leia Organa",
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
"friends": [
{ "name": "Luke Skywalker" },
{ "name": "Han Solo" },
{ "name": "C-3PO" },
{ "name": "R2-D2" }
]
}
]
}
} The query FetchLukeQuery {
human(id: "1000") {
name
}
} to get {
"human": {
"name": "Luke Skywalker"
}
} Alternately, we could have defined the query to have a query parameter: query FetchSomeIDQuery($someId: String!) {
human(id: $someId) {
name
}
} This query is now parameterized by Notice that the key in the response is the name of the field, by default. It is sometimes useful to change this key, for clarity or to avoid key collisions when fetching the same field with different arguments. We can do that with field aliases, as demonstrated in this query: query FetchLukeAliased {
luke: human(id: "1000") {
name
}
} We aliased the result of the {
"luke": {
"name": "Luke Skywalker"
}
} Notice the key is "luke" and not "human", as it was in our previous example where we did not use the alias. This is particularly useful if we want to use the same field twice with different arguments, as in the following query: query FetchLukeAndLeiaAliased {
luke: human(id: "1000") {
name
}
leia: human(id: "1003") {
name
}
} We aliased the result of the first {
"luke": {
"name": "Luke Skywalker"
},
"leia": {
"name": "Leia Organa"
}
} Now imagine we wanted to ask for Luke and Leia's home planets. We could do so with this query: query DuplicateFields {
luke: human(id: "1000") {
name
homePlanet
}
leia: human(id: "1003") {
name
homePlanet
}
} but we can already see that this could get unwieldy, since we have to add new fields to both parts of the query. Instead, we can extract out the common fields into a fragment, and include the fragment in the query, like this: query UseFragment {
luke: human(id: "1000") {
...HumanFragment
}
leia: human(id: "1003") {
...HumanFragment
}
}
fragment HumanFragment on Human {
name
homePlanet
} Both of those queries give this result: {
"luke": {
"name": "Luke Skywalker",
"homePlanet": "Tatooine"
},
"leia": {
"name": "Leia Organa",
"homePlanet": "Alderaan"
}
} The We defined the type system above, so we know the type of each object in the
output; the query can ask for that type using the special field query CheckTypeOfR2 {
hero {
__typename
name
}
} Since R2-D2 is a droid, this will return {
"hero": {
"__typename": "Droid",
"name": "R2-D2"
}
} This was particularly useful because query CheckTypeOfLuke {
hero(episode: EMPIRE) {
__typename
name
}
} We would find that it was Luke, who is a Human: {
"hero": {
"__typename": "Human",
"name": "Luke Skywalker"
}
} As with the type system, this example just scratched the surface of the query language. The specification goes into more detail about this topic in the "Language" section, and the language directory in GraphQL.js contains code implementing a specification-compliant GraphQL query language parser and lexer. ValidationBy using the type system, it can be predetermined whether a GraphQL query is valid or not. This allows servers and clients to effectively inform developers when an invalid query has been created, without having to rely on runtime checks. For our Star Wars example, the file starWarsValidationTests.js contains a number of demonstrations of invalid operations, and is a test file that can be run to exercise the reference implementation's validator. To start, let's take a complex valid query. This is the query NestedQueryWithFragment {
hero {
...NameAndAppearances
friends {
...NameAndAppearances
friends {
...NameAndAppearances
}
}
}
}
fragment NameAndAppearances on Character {
name
appearsIn
} And this query is valid. Let's take a look at some invalid queries! When we query for fields, we have to query for a field that exists on the given
type. So as # INVALID: favoriteSpaceship does not exist on Character
query HeroSpaceshipQuery {
hero {
favoriteSpaceship
}
} is invalid. Whenever we query for a field and it returns something other than a scalar or an
enum, we need to specify what data we want to get back from the field. Hero
returns a # INVALID: hero is not a scalar, so fields are needed
query HeroNoFieldsQuery {
hero
} Similarly, if a field is a scalar, it doesn't make sense to query for additional fields on it, and doing so will make the query invalid: # INVALID: name is a scalar, so fields are not permitted
query HeroFieldsOnScalarQuery {
hero {
name {
firstCharacterOfName
}
}
} Earlier, it was noted that a query can only query for fields on the type in
question; when we query for # INVALID: primaryFunction does not exist on Character
query DroidFieldOnCharacter {
hero {
name
primaryFunction
}
} That query is invalid, because query DroidFieldInFragment {
hero {
name
...DroidFields
}
}
fragment DroidFields on Droid {
primaryFunction
} This query is valid, but it's a bit verbose; named fragments were valuable above when we used them multiple times, but we're only using this one once. Instead of using a named fragment, we can use an inline fragment; this still allows us to indicate the type we are querying on, but without naming a separate fragment: query DroidFieldInInlineFragment {
hero {
name
... on Droid {
primaryFunction
}
}
} This has just scratched the surface of the validation system; there are a number of validation rules in place to ensure that a GraphQL query is semantically meaningful. The specification goes into more detail about this topic in the "Validation" section, and the validation directory in GraphQL.js contains code implementing a specification-compliant GraphQL validator. IntrospectionIt'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! For our Star Wars example, the file starWarsIntrospectionTests.js 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. We designed the type system, so we know what types are available, but if we
didn't, we can ask GraphQL, by querying the query IntrospectionTypeQuery {
__schema {
types {
name
}
}
} and we get back: {
"__schema": {
"types": [
{
"name": "Query"
},
{
"name": "Character"
},
{
"name": "Human"
},
{
"name": "String"
},
{
"name": "Episode"
},
{
"name": "Droid"
},
{
"name": "__Schema"
},
{
"name": "__Type"
},
{
"name": "__TypeKind"
},
{
"name": "Boolean"
},
{
"name": "__Field"
},
{
"name": "__InputValue"
},
{
"name": "__EnumValue"
},
{
"name": "__Directive"
}
]
}
} Wow, that's a lot of types! What are they? Let's group them:
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! query IntrospectionQueryTypeQuery {
__schema {
queryType {
name
}
}
} and we get back: {
"__schema": {
"queryType": {
"name": "Query"
}
}
} And that matches what we said in the type system section, that the It is often useful to examine one specific type. Let's take a look at the
query IntrospectionDroidTypeQuery {
__type(name: "Droid") {
name
}
} and we get back: {
"__type": {
"name": "Droid"
}
} What if we want to know more about Droid, though? For example, is it an interface or an object? query IntrospectionDroidKindQuery {
__type(name: "Droid") {
name
kind
}
} and we get back: {
"__type": {
"name": "Droid",
"kind": "OBJECT"
}
}
query IntrospectionCharacterKindQuery {
__type(name: "Character") {
name
kind
}
} and we get back: {
"__type": {
"name": "Character",
"kind": "INTERFACE"
}
} 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 query IntrospectionDroidFieldsQuery {
__type(name: "Droid") {
name
fields {
name
type {
name
kind
}
}
}
} and we get back: {
"__type": {
"name": "Droid",
"fields": [
{
"name": "id" |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论