Delve into the usage syntax of GraphQl

The syntax for GraphQl was outlined in the previous section, but this article expands on the previous tutorial and tries to cover all the syntax for GraphQl.

1. Terminal syntax

We first introduce the syntax used in the front-end Query, including Query and Mutation, and we also introduce Subscription as similar to Query.

1.1 the Query

Suppose we now have a data set with a structure like this:

  1. Students and teachers each have their own unique fields;
  2. There’s one way for the students to get all the relevant teachers, and there’s one way for the teachers to get all the students;
  3. Students and teachers are many-to-many relationships;
classDiagram
    Student <|-- Teacher2Student
    Teacher <|-- Teacher2Student

    class Student {
        +Int id
        +String name
        +Int age
        +teachers(id_eq: number) get_all_teachers
    }
    class Teacher {
        +Int id
        +String name
        +Boolean gender
        +students(name_contains: string) get_all_students
    }
    class Teacher2Student {
        +Int id
        +Int student_id
        +Int teacher_id
        +student() get_student
        +teacher() get_teacher
    }

First of all, the simplest way to use it can be checked:

Query {student {id name teacher {id name}}} # {data: {student: [{id: 1, name:], teacher: [{id: 1] 1, name: "teacher li"}, {id: 2, name: "Mr Wu"}}, {id: 2, name: "bill," the teacher: [{id: 1, name: "teacher li"}]}, {id: 3, the name: "three", the teacher: [{id: 2, name: "Mr Wu"}]}}}]

According to the above query, we can find that there are two students in total, and Miss Li taught them both at the same time. This is the most basic usage. Next we slowly add query requirements to change the results.

1.1.1 parameters the Arguments

We can limit the contents of the returned set by adding parameters

Query {student(Name_Contains: 3) {# Name teacher(id_eq: 3) {# Name teacher(id_eq: 3); {data: {student: [{id: 1, name: "1 ", teacher: [{id: 2, name:" 2 "] {data: {student: [{id: 1, name: "2"], teacher: [{id: 2, name: "2"] "Mr Wu"}}, {id: 3, name: "three", the teacher: [{id: 2, name: "Mr Wu"}]}]}}

At this point, because we filter the teacher as long as the ID is 2, so teacher Li will be filtered; Because it was set up to filter only students with “three” in their names, Li Si was also filtered out.

Similarly, you can also use parameters to page, skip, and other operations on the content, the same operations do not write the example.

1.1.2 alias Aliases

Because different data entities in the query itself are similar to directly requesting the existence of the unit in the GraphQl statement, it will report an error if you request two identical sets at the same time; Because they’re all supposed to correspond one to one. You can use an alias to solve the problem:

Query {SAN: student(Name_contains: ") {id name} Wang: student(Name_contains: ") {id name} # {SAN: [{id: 1, name: "* *"}] wang: [{id: 3, name: "three"}]}}

When processing the result of the request, be aware that the aliased content is returned with the alias key, not the original name.

1.1.3 pieces Fragments could

Looking at the query above, we can see that when we use different aliases, we inevitably have a lot of duplicate field names. This example is fine with fewer fields, but it’s common for businesses to have dozens of fields, which would be too taxing, so we can use Fragments to handle them:

Fragment studentFields on Student {id name} query {SAN: Student (Name_contains: "3 ") {... StudentFields} wang: Student (Name_contains: contains) {... StudentFields}} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields} # StudentFields}

1.1.4 Operation Name

This is relatively rare, at least in my personal use, I tend to “save as much as you can” principle; But if I’m writing a tutorial, I’ll just introduce it. Mainly occurs in the case of multiple operations at the same time, used to distinguish operation data.

Query op1 {student(Name_contains: ") {id}} query op2 {student(Name_contains: ") {id}} # op1. {student {id}} {student {id}} {student {id}}

1.1.5 Operation parameter Variables

This parameter is different from the Arguments mentioned above, which are used for specific data node operations. Variables refers to operator-oriented Variables that make Query reusable and easy to use in different places.

Let’s say we have two different pages, both of which want to query the student table but have different filters, and then if we write two queries like this it would be wasteful, it would be ugly, and it wouldn’t be reusable.

Query {student(Name_contains: ") {Id}} Query {student(Name_contains: ") {Id}}

Because essentially, they query the same content, but the parameters are a little different, here we can extract the parameters, by the actual use of different cases and then pass the parameters:

Query ($name: String) {student(Name_contains: $name) {id}}

Change the Variables you pass in when using it. Example:

const query = gql` query($name: String) { student(name_contains: $name) { id } } ` const page1 = post(URL, query=query, variables={name: Const page2 = post(URL, query=query, variables={name: "var "})

The result is the same as writing two different Queries above, but the code is much more elegant and the Queries are reasonably reused. If one day you need to change the return result of a request, you don’t have to go around and change the request Query one by one.

Note that there are several hard and fast rules for defining parameters:

  1. Parameter names should start with $. It’s not negotiable. It doesn’t recognize it if it starts with a dollar sign.
  2. The type of the parameter must be the same as the type where it will be used, otherwise an error will occur. Because GraphQl is a statically typed language.
  3. Default values for parameters can be given in a manner similar to TS, as follows.
Query ($name: String = $name) {student(Name_contains: $name) {id}}

1.1.6 Directives

Directives can be translated into Directives, but I don’t find them intuitive. They mostly function like a conditional modifier, similar to the if-else block in code. Allows you to specify the details of how to request on the outside.

query($name: String, $withTeacher: Boolean!) {
  student(name_contains: $name) {
    id
    teacher @include(if: $withTeacher) {
      id
    }
  }
}

Its main function is to say that if you give withTeacher=true in the variables outside then it will request the teacher node, which is equivalent to:

query($name: String) {
  student(name_contains: $name) {
    id
    teacher {
      id
    }
  }
}

Otherwise, if you specify withTeacher=false, it will omit the teacher node, which is equivalent to:

query($name: String) {
  student(name_contains: $name) {
    id
  }
}

Directives have two main operators: @include(if: Boolean) and @skip(if: Boolean)

The two have opposite effects. In addition, Directives this function requires the server to have relevant support to use. But at the same time, the server can also implement fully customized Directives on its own if needed.

1.2 Mutation

1.2.1 Operation parameter Variables

This is exactly the same rule as in Query. See above for a small example:

($name: String, $age: 18) {no mutation create($name: String, $age: 18) {no mutation create($name: String, $age: 18) {no mutation create($name: String, $age: 18)}) Int) { createStudent(name: $name, age: $age) {id}} # create createStudentInput ($input: createStudentInput!) {return createStudentInput (); { createStudent($input) { id } }

1.2.2 Inline Fragments

The usage scenario here is primarily for the Union type, similar to the relationship between an interface and a class.

Suppose we have an interface called Animal, and there are two classes called Dog and Bird. And we give these two classes out by a GraphQl node:

{ animal { name kind ... on Dog { breed } ... On Bird {wings}} # result {data: {animal: [{name: "Pepe", kind: "Dog", breed: "Husky"}, {name: "Pipi", kind: "Husky"} "Bird", wings: 2 } ] } }

As you can see from the above results, it can look up different “classes” by different types, but when returned, it can be combined. It’s like getting the data of the implementation class directly from an “interface”. Very concrete. But most of the time we might not combine two different structures and return one array, we might use the same node name to look for different things but filter them by their type first.

1.2.3 Meta Fields

With the example above, if we don’t have the kind field, how do we know which element is which type? We can use meta-fields to know which data entity we are working on. The main meta-fields are __typename.

We can check like this:

{ animal { name __typename ... on Dog { breed } ... On Bird {wings}}} # result {data: {animal: [{name: "Pepe", __typename: "Animal__Dog", breed: "Husky"}, {name: : "Pipi", __typename: "Animal__Bird", wings: 2 } ] } }

__typename is built-in, you can look it up on any node and it will give you a type.

2. Type definition

2.1 basis

We know that GraphQl is a statically typed syntax system, so we have to define its type before we can actually use it. GraphQl’s type definition is called Schemas. It has its own independent syntax. There are various basic types, Scalar, which can be defined as types for different objects. You can also define new types from the base types yourself.

All the different objects eventually form a tree structure, which is rooted in a schema:

schema {
  query: Query
  mutation: Mutation
}

Then we can define a layer of child objects inside. For example, our model above could be written as:

type Query { student(name_contains: String): Student teacher(id_eq: ID): Teacher } type Student { id: ID! name: String! age: Int teachers: [Teacher!] ! } type Teacher { id: ID! name: String! gender: Boolean }

As above we have defined two different objects and their properties. Fields that are required or not empty are marked with “!” After its type, such as id: id! That means id is a non-empty field. A non-null field will get an error if it is passed NULL during an operation. An array of other types can be composed of the type followed by brackets, such as Teacher in Student above.

Define a field as an array:

myField: [String!]

The field itself can be NULL, but it cannot have NULL members. Such as:

const myField: null // valid
const myField: [] // valid
const myField: ['a', 'b'] // valid
const myField: ['a', null, 'b'] // error

But if I define it like this:

myField: [String]!

That means it cannot be NULL itself but can contain NULL among its constituent members.

const myField: null // error
const myField: [] // valid
const myField: ['a', 'b'] // valid
const myField: ['a', null, 'b'] // valid

2.2 Built-in Type

GraphQl has only five built-in types by default. Respectively is:

ID: Like the ID field in a traditional database, it is used to distinguish between different objects. It can be an Int directly, or it can be a encoded unique value. For example, in common relay, the string “class name :ID” is used as the ID after base64 transcoding. Note that this ID only exists in GraphQl. It doesn’t necessarily correspond to what’s in the database. In the case of relay, the database might still have an integer and not the string.

Int: integer, can be positive or negative.

Float: A double precision floating point number that can be positive or negative

String: UTF-8 character String.

Boolean: true / false.

If that doesn’t fit your business scenario you can either customize a new type, or find a third party to extend the type.

It’s easy to write GraphQl to define a type. Let’s say we add a Date type.

scalar Date

That’s it, but you still have to implement what it does in your code, how it transitions in and out of the runtime, and so on.

In addition, enumerated types are supported in GraphQl, which can be defined like this:

enum GenderTypes {
  MALE
  FEMALE
  OTHERS
}

2.3 Interface and Union Types

Interface and Union are very similar, so I’m going to put them together.

Interface is similar to other languages in that it is used to define a common parent type. It can be defined and used like this:

interface Animal {
  id: ID!
  name: String
}

type Dog implements Animal {
  id: ID!
  name: String
  breed: String
}

type Cat implements Animal {
  id: ID!
  name: String
  color: String
}

As you can see, each field defined by the interface is implemented with it, but it can also have its own field. When querying Animal, it is important to note that you cannot directly look up individual fields on Animal, because the system does not know whether the object you are querying is Dog or Cat. You need to specify it with an inline fragment.

# "Cannot query field \"color\" on type \"Animal\". Did you mean to use an inline fragment on \"Cat\"? Query {animal {id name color}} # Query {animal {id name color}} # on Cat { color } } }

After talking about Interface, let’s look at Union. Union can be directly understood as an Interface without common fields.

union Plant = Lily | Rose | Daisy

The query uses Inline Fragments to specify the type just as the interface does.

2.4 Input types

We mentioned a little bit above in the Mutation Variables example, where all parameters of input to an operation are specified as a type, which makes it easier to add content and increases the degree to which code is reusable.

Let’s say we have a Mutation defined like this:

type Mutaion {
  createSomething(foo: Int, bar: Float): Something
}

Using the Input types:

input CreateSomethingInput {
  foo: Int
  bar: Float
}

type Mutaion {
  createSomething(input: CreateSomethingInput): Something
}