New to GraphQL

graphql

What is GraphQL?

GraphQL is both a query language for APIs and a runtime that satisfies your data queries (from: official explanation )

It is understood that GraphQL has its own query syntax. In the API request initiated, the query statement is passed to tell the server what operations and specific data fields are required. GraphQL defines the implementation specification, and various languages ​​implement the GraphQL functional framework. Through the framework The query syntax can be interpreted and executed, and then returned data output to the client

graphql


Advantages of GraphQL

All the following queries and outputs are from my DEMO, the implementation and source code Github address of DEMO will be mentioned below

Syntactic features to meet various needs

  • Supports multiple operations: query->query, mutation->modification, the specification is written before the query statement, the default is query if not written
  • Support parameters, realize various functions, such as: query data, sorting, paging, ... etc.
  • Syntax Other features, aliases, fragments, define variables, directives, ... etc.
# 查询语句-有参数
query{
  student(id:86){
    id
    name
    sclass{
      id
      num
      level
      heads
    }
  }
}
# 输出
{
  "data": {
    "student": {
      "id": 86,
      "name": "Emma",
      "sclass": {
        "id": 9,
        "num": 8,
        "level": 3,
        "heads": 68
      }
    }
  }
}
# 修改
mutation {
  update(id: 86, name: "66666") {
    rt
    msg
  }
}
# 输出
{
  "data": {
    "update": {
      "rt": 1,
      "msg": "bingo"
    }
  }
}

Query friendliness, query and output association

Does the query look a bit like JSON? The query syntax is in JSON format, and both front and back ends can be easily used. The query statement and the output data are closely related. By analyzing the query statement, you can know the content fields of the output data.


Flexibility, request the data you want, no more, no less

You can customize the query statement to get the fields you need to use, avoid the output of useless fields, and reduce unnecessary data blocks/data field query logic

multiple fields

# 查询语句
query{
  students{
    id
    name
    classid
    sclass{
      id
      num
      level
      heads
    }
  }
}
# 输出
{
  "data": {
    "students": [
       {
        "id": 19,
        "name": "Savannah",
        "classid":22,
        "sclass": {
          "id": 22,
          "num": 6,
          "level": 4,
          "heads": 57
        }
      },
      {
        "id": 34,
        "name": "Ariana",
        "classid":33,
        "sclass": {
          "id": 33,
          "num": 3,
          "level": 4,
          "heads": 57
        }
      }
    ]
  }
}

Removed the unused field output, and without the field sclass, you can do no sclass data query

# 查询语句
query{
  students{
    id
    name
  }
}
# 输出
{
  "data": {
    "students": [
      {
        "id": 19,
        "name": "Savannah"
      },
      {
        "id": 34,
        "name": "Ariana"
      }
    ]
  }
}

API evolution, no need to divide versions

There is no need to distinguish the version number for API version iteration. Adding fields does not affect existing queries. The requester can define the desired query information by himself.

# Say No
http://api.xxx.com/student/v1/
http://api.xxx.com/student/v2/
# ... 

Self-checking, can query and output all definitions

This is a very nice feature of GraphQL, that is, the GraphQL service API can query the types it supports through a statement. Development does not need to spend time writing API documents, and GraphQL directly helps developers quickly understand the API.

# 查询语句
{
  __type(name: "MStudentType") {
    kind
    name
    fields {
      name
      description
      type {
        name
      }
    }
  }
}
# 输出
{
  "data": {
    "__type": {
      "kind": "OBJECT",
      "name": "MStudentType",
      "fields": [
        {
          "name": "id",
          "description": "学号",
          "type": {
            "name": null
          }
        },
        {
          "name": "name",
          "description": "学生名",
          "type": {
            "name": null
          }
        },
        {
          "name": "age",
          "description": "年龄",
          "type": {
            "name": null
          }
        },
        {
          "name": "birthdate",
          "description": "生日",
          "type": {
            "name": null
          }
        },
        {
          "name": "sclass",
          "description": "班级信息",
          "type": {
            "name": "MClassType"
          }
        }
      ]
    }
  }
}

Based on self-inspection, GraphQL has open sourced the auxiliary tool GraphiQL, which is convenient for GraphQL interface debugging and automatic generation of interface documentation.

  • GraphQL auxiliary tool: GraphiQL, which can debug query statements and visualize documents for the schema defined by the interface
    • Query statement for perception
    • Error message
    • Statement formatting
    • execute query
    • View the schema document information defined by the interface

The GraphiQL in the graphql-dotnet open source project needs to be connected to the GraphQL interface developed by itself, and simple modifications and adjustments are required, which will be discussed later.


Getting Started Tutorial under .NET

  • Building an ASP.NET MVC5 WebAPI project
  • NutGet import package
  • Simple implementation of a student query API based on GraphQL
    • Support query student information list
    • Support for querying students' class information
    • Support for querying student information corresponding to student number
    • Support for changing student names

Definition [data class] MStudent.cs (student class), MClass.cs (class class), MResult.cs (execution result class)


public class MStudent {
    /// <summary>
    /// 学号
    /// </summary>
    public int Id { get; set; }
    /// <summary>
    /// 名字
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 年龄
    /// </summary>
    public int Age { get; set; }
    /// <summary>
    /// 所在班级编号
    /// </summary>
    public int ClassId { get; set; }
    /// <summary>
    /// 生日
    /// </summary>
    public DateTime Birthdate { get; set; }
    /// <summary>
    /// 班级
    /// </summary>
    public MClass SClass { get; set; }
}

public class MClass {
    public int Id { get; set; }
    /// <summary>
    /// 年级
    /// </summary>
    public int Level { get; set; }
    /// <summary>
    /// 第几班
    /// </summary>
    public int Num { get; set; }
    /// <summary>
    /// 总人数
    /// </summary>
    public int Heads { get; set; }
}

public class MResult {
    /// <summary>
    /// 输出结果,0=失败,1=成功
    /// </summary>
    public int rt { get; set; }
    /// <summary>
    /// 说明信息
    /// </summary>
    public string msg { get; set; }
}

Define GraphType classes MStudentType, MClassType, MResultType to inherit ObjectGraphType<TSourceType>, TSourceType generic type corresponds to the [Data Class]
constructor to add data fields that can be queried through Field, including: description and the processing method of field content acquisition, etc.


public class MStudentType : ObjectGraphType<MStudent> {
    private static BStudent _bll { get; set; }
    public MStudentType() {
        if (_bll == null) _bll = new BStudent();
        Field(d => d.Id).Description("学号");
        Field(d => d.Name).Description("学生名");
        Field(d => d.Age).Description("年龄");
        Field(d => d.Birthdate).Description("生日");
        Field<MClassType>("sclass", resolve: d => {
            //缓存中已经存在就直接返回
            if (d.Source.SClass != null) return d.Source.SClass;
            //从DB/缓存中获取数据
            var classId = d.Source?.ClassId ?? 0;
            if (classId > 0) d.Source.SClass = _bll.GetClass(d.Source.ClassId);
            return d.Source.SClass;
        },description:"班级信息");
    }
}

public class MClassType : ObjectGraphType<MClass> {
    public MClassType() {
        Field(d => d.Level).Description("年级");
        Field(d => d.Heads).Description("人数");
        Field(d => d.Id).Description("编号");
        Field(d => d.Num).Description("班级");
    }
}

public class MResultType : ObjectGraphType<MResult> {
    public MResultType() {
        Field(d => d.rt);
        Field(d => d.msg);
    }
}

Define the operation class (query/mutation) of Schema, inherit ObjectGraphType, including: StudentQuery, StudentMutation


public class StudentQuery : ObjectGraphType {
        public StudentQuery(BStudent bll) {
            //查询-有参数id
            Field<MStudentType>("student", arguments: new QueryArguments(new QueryArgument<IntGraphType>() {
                Name = "id"
            }), resolve: d => {
                var id = d.Arguments["id"].GetInt(0, false);
                return bll.GetModel(id); ;
            });
            //查询-列表
            Field<ListGraphType<MStudentType>>("students", resolve: d => {
                return bll.GetStudents();
            });
        }
    }
}

public class StudentMutation : ObjectGraphType {
    public StudentMutation(BStudent bll) {
        Field<MResultType>("update", arguments: new QueryArguments(
            new QueryArgument<IntGraphType> {
                Name = "id"
            },
            new QueryArgument<StringGraphType> {
                Name = "name"
            }
        ), resolve: (d) => {
            var id = d.Arguments["id"].GetInt(0, false);
            var name = d.Arguments["name"].GetString("");
            if (id <= 0) return new MResult {
                rt = 0,
                msg = "非法学号"
            };
            if (name.IsNullOrWhiteSpace()) return new MResult {
                rt = 0,
                msg = "非法名字"
            };
            var isSc = bll.UpdateName(id, name);
            if (!isSc) return new MResult {
                rt = 0,
                msg = "更新失败"
            };
            return new MResult {
                rt = 1,
                msg = "bingo"
            };
        });
    }
}

Add an interface in the controller, construct a Schema object, parse and execute the returned result according to the query conditions, and output
Query = StudentQuery, Mutation = StudentMutation

/// <summary>
/// graphql demo 接口
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("query")]
public object Test_Query() {
    var r = HttpContext.Current.Request;
    var query = r.GetF("query");
    var bll = new BStudent();
    var schema = new Schema { Query = new StudentQuery(bll), Mutation = new StudentMutation(bll) };
    var result = new DocumentExecuter()
        .ExecuteAsync(options => {
            options.Schema = schema;
            options.Query = query;
        }).GetAwaiter();
    var json = new DocumentWriter(indent: true).Write(result);
    return result.GetResult();
}

Access to GraphiQL tools

  • Git Clone graphql-dotnet
  • Install NodeJS environment
  • The command tool CMD opens graphql-dotnet/src/GraphQL.GraphiQL/ and executes the following command
    • npm install -g yarn
    • yarn install
    • yarn start
  • Run graphql-dotnet/src/GraphQL.GraphiQL to start: http://localhost:47080/
  • Adjust according to your own situation: graphql-dotnet/src/GraphQL.GraphiQL/app/app.js script, such as the code posted below
    • url=graphql interface address
    • If you need to put it into production use, you can pass in the interface address as a url, or support the input box to input the address, and the interface address is not fixed.
  • After each adjustment, you need to re-execute yarn start, the front-end will use webpack for packaging operations, and you can refresh the page after the execution is complete.

//调整如下
import React from 'react';
import ReactDOM from 'react-dom';
import GraphiQL from 'graphiql';
import axios from 'axios';
import 'graphiql/graphiql.css';
import './app.css';

function graphQLFetcher(graphQLParams) {
    console.log(graphQLParams["query"]);
    return axios({
        method: 'post',
        url: "http://127.0.0.1:5656/query",//window.location.origin + '/api/graphql',
        data: "query=" + graphQLParams["query"],
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    }).then(resp => resp.data);
}
ReactDOM.render(<GraphiQL fetcher={graphQLFetcher} />, document.getElementById('app'));

graphql

Summarize

  • For the application, I think it can be used in new project requirements, or existing suitable application scenarios can be refactored, and the service can run stably and can be used on a large scale after the development is started.
  • For the comparison of RestFul and GraphQL, I think there is no best protocol, only the most suitable scenario

resource


First published on my independent blog

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324929177&siteId=291194637