• Translation time: March /21, 2019
  • Translator: He Ruifeng (in-depth use of GraqhQL)
  • Purpose: To provide more down-to-earth Chinese instructions plus: Other translations suck

! Welcome to write to discuss GraphQL with your translator!

Schemas and Types

In this article, you’ll learn all the details of the GraphQL type system and how it describes what kind of data can be queried. Since GraphQL can be used in any back-end framework and programming language, let’s leave out the implementation details of GraphQL and focus on the core concepts.

Type System

If you’ve seen GraphQL Query, you know that the GraphQL query language basically queries a specified field on an object. For example,

  • query
    {
      hero {
        name
        appearsIn
      }
    }
    Copy the code
  • result
    {
      "data": {
        "hero": {
          "name": "R2-D2"."appearsIn": ["NEWHOPE"."EMPIRE"."JEDI"]}}}Copy the code
  1. We start with a special “root” object
  2. Make a choice on the Hero lot
  3. For the objects returned by Hero, we select the Name and appearsIn fields

Because GraphQL’s queries and results are structurally highly matched, you can predict what data the server will return regardless of how the server implements it. But it’s useful to have an accurate description of the data we’re looking for — which fields can we query? What kind of objects will be returned? Which fields are available in the child object? That’s what schema does.

Each GraphQL Service defines a set of types that completely describe the set of data you can access. Then, when the query is received, the request is validated and executed based on the Schema.

Type language

GraphQL Services can be implemented in any language, and since we don’t rely on the syntax of a particular programming language, such as JavaScript to teach GraphQL Schemas, We will define our own simple language –“GraphQL Schema Language “, which is similar to QL language, so that we can communicate well with GraphQL Schemas.

Object types and fields

The most basic component of GraphQL Schema is object Types, which identifies which objects and subfields you can retrieve from back-end services. Such as:

typeCharacter { name: String! appearsIn: [Episode!] ! }Copy the code

The language is highly readable, but let’s go over the details to keep the basic terminology in perspective

  • Character is a GraphQL Object Type, which means that the Object has some fields. Most types in your schema are Object types
  • Name and appearsIn are Character types, which means that only Name and appearsIn can be used when GraphQL Query operates on ** Character Type **.
  • String is one of the built-in scalar types **, which resolve to a single scalar object and has no secondary options. We’ll discuss this in more detail later
  • String! The field must have a value when you issue a GraphQL Query. In type languages, we use an exclamation mark to indicate this.
  • [Episode!] ! The array representing Episode Objects is a non-empty array. If the appearsIn field is requested, an array must be passed and every data in the data must be of the Episode type.

Now you know what the GraphQL Object Type looks like and how to read the language.

Arguments

Each GraphQL Object Type can have parameters such as:

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}
Copy the code

Each parameter has a name. Unlike JavaScript and Python functions that receive an ordered set of arguments, arguments in GrapphQL are passed with names specified. In the above example, the length field has a defined parameter unit.

The parameter can be required or optional, and when it is, we define a default value ahead of time — if the unit parameter is not passed, it will use the default METER.

The Query and Mutation types

There are two very specific types in your schema

schema {
  query: Query
  mutation: Mutation
}
Copy the code

Every GraphQL service has a query type, and the mutation type does not always exist. In particular, the mutation type defines the entry of each GraphQL query

query {
  hero {
    name
  }
  droid(id: "2000") {
    name
  }
}
Copy the code

This means that your GraphQL Service must have a Query Type that contains the Hero and Droid fields

typeQuery { hero(episode: Episode): Character droid(id: ID!) : Droid }Copy the code

Mutations work similarly, too. You define fields on Mutation type, which are the entry points for your Mutation.

Keep in mind that Query and Mutation types are no different from normal object types, except that they can define entries.

Scalar types

A GraphQL Object Type has names and fields, but at some point these fields must be parsed into some concrete data. For example, the appearsIn field is returned in the array [“NEWHOPE”,”EMPIRE”,”JEDI”].

GraqhQL has a built-in collection of Scalar Types

  • Int: a 32-bit signed integer
  • Float: a double – precision signed floating point number
  • String: UTF-8 The value is a character String
  • Boolean: true of false
  • ID: Represents a special identifier, often used to retrieve a particular object or as a key identifier in the cache. The ID type is serialize in the same way as String; But when we define ids, we don’t want them to be readable

In most GraphQL Service implementations, there will often be a special custom Scalar Type. For example, we can define a Date type:

scalar Date
Copy the code

Then we can define for ourselves how to serialize, deserialized and validate. For example, you can specify that Date Type is serialize as an integer timestamp.

Enumeration types

Also known as Enums, this type is a special scalar types, which can only be selected from a specific set of values. The effect of this is

  • Verify that parameters of this type can only be specified values
  • Throughout the type system, there are always a limited number of values for this field to choose from

What does an enumeration definition look like in GraphQL Schema Language?

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
Copy the code

No matter where you use Type Episode, we think it only has those values.

Note: GraphQL Services implemented in various languages have their own way of handling enumerated types. In languages that treat Enums as first-class citizens, this implementation can take advantage of the above features. But in a multipoint language like JavaScript that does not support enum, its value may be mapped to a set of integers. However, the client does not know these details, and the client operates entirely on the string name of the enum value.

Lists and Non-Null

This paragraph belongs to nonsense and much, knowledge point can be deduced from the front again. I’m not going to do the translation, but you can actually skip over here. If you are not at ease, please refer to the original text

Interfaces

Like other types of systems, GraphQL supports Interfaces. An interface is an abstract type that contains a certain set of fields. When you do something like Type A inmlements B (Interface), type A must contain the fields defined in the INTERFACE B. Let’s take an example

Define an interface ahead of time and wait for other types to implement,

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
Copy the code

Any Implement Character type must have the fields and parameters described above, as shown below

type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  # above is the Character interface
  starships: [Starship]
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  # above is the Character interface
  primaryFunction: String
}
Copy the code

Human and Droid can have their own fields in addition to those defined by the Character interface.

Interfaces are useful when you want to return an object or a list of object Types.

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) { name ... On Droid {primaryFunction}}}"ep": "JEDI"
}
Copy the code

The Hero field is pre-defined to return a Character Type, and only Human or Droid Implement has a Character Type, so for a Character type field built in – for example, name, You can retrieve it directly, but other implementation-specific fields such as primaryFunction on the Droid must be retrieved using inline fragments

Learn more about this in the inline fragments section in the query guide.

An interface is an abstract type, and all implementations of the interface have a common type field

Union types

The Union type is very similar to the interface type, but there is no common field between types specified

union SearchResult = Human | Droid | Starship
Copy the code

Whenever we return SearchResult Type, we can get the three types mentioned above. Note that the union type must be composed of the actual object type and cannot be composed of other union types or interfaces.

In this case, if you are querying for a field that returns a SerchResult Union type, you also need to use inline fragments

  • query
    {
      search(text: "an") {
        __typename
        ... on Human {
          name
          height
        }
        ... on Droid {
          name
          primaryFunction
        }
        ... on Starship {
          name
          length
        }
      }
    }
    Copy the code
  • result
{
  "data": {
    "search": [{"__typename": "Human"."name": "Han Solo"."height": 1.8}, {"__typename": "Human"."name": "Leia Organa"."height": 1.5}, {"__typename": "Starship"."name": "TIE Advanced x1"."length": 9.2}]}}Copy the code

The __typENAME field is mandatory String, used as an identifier to distinguish it from other data types.

And, in this case, since both Human and Droid have a common interface (Character), you can query their public fields directly instead of repeatedly querying them.

{
  search(text: "an") {
    __typename
    ... on Character {
      name
    }
    ... on Human {
      height
    }
    ... on Droid {
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}
Copy the code

Note here that name must still be specified in Starship, otherwise **name ** will not appear in the query result because it does not share the same interface.

Input types

So far, we have only discussed transferring Scalar values, such as Enums or strings, as parameters. But you can pass complex objects as arguments. This is useful in mutation, where you pass a large object to the server. In GraphQL Schema Language, input Type is just like any other regular object types, but instead of using the type keyword, input is used as the keyword.

input ReviewInput {
  stars: Int!
  commentary: String
}
Copy the code

Here is an example of how to use it

mutation CreateReviewForEpisode($ep: Episode! .$review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {stars commentary}}"ep": "JEDI"."review": {
    "stars": 5,
    "commentary": "This is a great movie!"}} // Return data {"data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"}}}Copy the code

Fields on the input object Type can also refer to other input object types, but the input/output type should not be confused. Input Object Type does not support parameter passing on fields.

!!!!!!!!! Complete!! All see here, translation is not easy, please leave your 👍! This will be my motivation to continue to provide quality articles

The client documentation has also been translated: juejin.cn/post/684490…