GraphQL入门指南

Introduction

GraphQL is Facebook’s new query language for fetching application data in a uniform way.

GraphQL并不是一个面向图数据库的查询语言,而是一个数据抽象层,包括数据格式、数据关联、查询方式定义与实现等等一揽子的东西。GraphQL也并不是一个具体的后端编程框架,如果将REST看做适合于简单逻辑的查询标准,那么GraphQL可以做一个独立的抽象层,通过对于多个REST风格的简单的接口的排列组合提供更多复杂多变的查询方式。与REST相比,GraphQL定义了更严格、可扩展、可维护的数据查询方式。

GraphQL与之前Netflix出品的Falcor,都是致力于解决相同的问题:如何有效处理日益增长不断变化的Web/Mobile端复杂的数据需求。笔者一直认为,REST原论文最大的功劳在于前后端分离与无状态请求,而REST的资源化的请求方式只适合面向简单的请求,对于具有复杂资源间关联的请求就有点无能为力。关于这一点,笔者在之前的RARF系列中有过充分的讨论。

GraphQL is a specification.

还是需要强调一点,引入GraphQL并不意味着要像之前从Struts迁移到SpringBoot一样需要去修改你的真实的后端代码,因此GraphQL可以看做一个业务逻辑层灵活有效地辅助工具。这一点也是GraphQL与原来的REST API最大的差别,举例而言:

{

  latestPost {

    _id,

    title,

    content,

    author {

      name

    },

    comments {

      content,

      author {

        name

      }

    }

  }

}

这是一个很典型的GraphQL查询,在查询中指明了需要返回某个Blog的评论与作者信息,一个典型的返回结果譬如:

{

  "data": {

    "latestPost": {

      "_id": "03390abb5570ce03ae524397d215713b",

      "title": "New Feature: Tracking Error Status with Kadira",

      "content": "Here is a common feedback we received from our users ...",

      "author": {

        "name": "Pahan Sarathchandra"

      },

      "comments": [

        {

          "content": "This is a very good blog post",

          "author": {

            "name": "Arunoda Susiripala"

          }

        },

        {

          "content": "Keep up the good work",

          "author": {

            "name": "Kasun Indi"

          }

        }

      ]

    }

  }

}

而如果采用REST API方式,要么需要前端查询多次,要么需要去添加一个新的接口,专门针对前端这种较为特殊的请求进行响应,而这样又不可避免地导致后端代码的冗余,毕竟很有可能这个特殊的请求与返回哪天就被废了。

Reference

Tutorials & Docs

Mechanism:原理介绍

Practices & Resources

Comparison:框架对比

Collection

Quick Start

Official Quick Start:官方的简单的Quick Start教程

Setup

首先创建项目文件夹:

mkdir graphql-demo
cd graphql-demo

然后使用npm安装必要的依赖:

npm init -f
npm install graphql express express-graphql --save

Data

作为一个简单的数据服务器,我们仅使用最简单的JSON文件作为数据源:

{
  "1": {
    "id": "1",
    "name": "Dan"
  },
  "2": {
    "id": "2",
    "name": "Marie"
  },
  "3": {
    "id": "3",
    "name": "Jessie"
  }
}

Server

一个简单的GraphQL服务器需要创建Scheme以及支持的查询:

// Import the required libraries
var graphql = require(‘graphql‘);
var graphqlHTTP = require(‘express-graphql‘);
var express = require(‘express‘);

// Import the data you created above
var data = require(‘./data.json‘);

// Define the User type with two string fields: `id` and `name`.
// The type of User is GraphQLObjectType, which has child fields
// with their own types (in this case, GraphQLString).
var userType = new graphql.GraphQLObjectType({
  name: ‘User‘,
  fields: {
    id: { type: graphql.GraphQLString },
    name: { type: graphql.GraphQLString },
  }
});

// Define the schema with one top-level field, `user`, that
// takes an `id` argument and returns the User with that ID.
// Note that the `query` is a GraphQLObjectType, just like User.
// The `user` field, however, is a userType, which we defined above.
var schema = new graphql.GraphQLSchema({
  query: new graphql.GraphQLObjectType({
    name: ‘Query‘,
    fields: {
      user: {
        type: userType,
        // `args` describes the arguments that the `user` query accepts
        args: {
          id: { type: graphql.GraphQLString }
        },
        // The resolve function describes how to "resolve" or fulfill
        // the incoming query.
        // In this case we use the `id` argument from above as a key
        // to get the User from `data`
        resolve: function (_, args) {
          return data[args.id];
        }
      }
    }
  })
});

express()
  .use(‘/graphql‘, graphqlHTTP({ schema: schema, pretty: true }))
  .listen(3000);

console.log(‘GraphQL server running on http://localhost:3000/graphql‘);

然后使用node命令启动服务器:

node index.js

如果你直接访问http://localhost:3000/graphql会得到如下反馈:

{
  "errors": [
    {
      "message": "Must provide query string."
    }
  ]
}

Queries

按照如下方式可以创建一个简单的根据ID查询用户的姓名,从中可以看出基本的GraphQL的查询的样式,就是一个JSON的Key-Value对,键值就是查询值:

{

  user(id: "1") {

    name

  }

}

返回数据是:

{

  "data": {

    "user": {

      "name": "Dan"

    }

  }

}

如果你希望以GET方式进行查询,可以移除所有的空格,即得到如下方式的请求:

http://localhost:3000/graphql?query={user(id:"1"){name}}

Another First GraphQL Server:另一个Step By Step的介绍

Setup an HTTP Server:构建一个HTTP服务器

注意,GraphQL定义了一种通用的数据查询语言,并不一定要基于HTTP协议,不过目前绝大部分应用服务器的交互协议都是HTTP,因此这里也是基于Express以及GraphQL的JavaScript实现构建一个简单的GraphQL服务器。

$ mkdir graphql-intro && cd ./graphql-intro

$ npm install express --save

$ npm install babel --save

$ touch ./server.js

$ touch ./index.js

而核心的服务端代码为:

// index.js

// by requiring `babel/register`, all of our successive `require`s will be Babel‘d

require(‘babel/register‘);

require(‘./server.js‘);

// server.js

import express from ‘express‘;

let app  = express();

let PORT = 3000;

app.post(‘/graphql‘, (req, res) => {

  res.send(‘Hello!‘);

});

let server = app.listen(PORT, function () {

  let host = server.address().address;

  let port = server.address().port;

  console.log(‘GraphQL listening at http://%s:%s‘, host, port);

});

直接使用Node命令即可以启动服务器:

$ node index.js
GraphQL listening at http://0.0.0.0:3000

可以用Curl进行简单的测试:

$ curl -XPOST http://localhost:3000/graphql

Hello!

创建一个Schema


现在我们已经创建了一个简单的HTTP Server可以进行交互,下面我们就要为该Server添加GraphQL查询的解析的支持。首先回顾下一个基本的GraphQL的查询请求如下:

query getHighScore { score }

该查询意味着某个GraphQL的客户端希望获取getHighScore域的score子域的信息,Fields就是客户端要求GraphQL返回的数据说明,一个Fields也可以包含参数,譬如:

query getHighScores(limit: 10) { score }

而我们的GraphQL Server首先需要知道应该如何去解析这样的请求,即需要去定义Schema。构建一个Schema的过程有点类似于构建RESTful的路由树的过程,Schema会包含Server可以返回给前端的Fields以及响应中的数据类型。GraphQL中是采取了静态数据类型,因此Client可以依赖于其发起请求时声明的数据类型。首先我们声明使用Schema所需要的依赖项:

$ npm install graphql --save
$ npm install body-parser --save
$ touch ./schema.js

然后我们创建一个GraphQLSchema实例,一般来说我们会将配置放入一个单独的文件夹中:

// schema.js

import {

  GraphQLObjectType,

  GraphQLSchema,

  GraphQLInt

} from ‘graphql/lib/type‘;

let count = 0;

let schema = new GraphQLSchema({

  query: new GraphQLObjectType({

    name: ‘RootQueryType‘,

    fields: {

      count: {

        type: GraphQLInt,

        resolve: function() {

          return count;

        }

      }

    }

  })

});

export default schema;

该Schema的定义用通俗地语言表达即是针对查询会返回一个RootQueryType的对象,而每个RootQueryType对象会包含一个整型的count域。

Connect the Schema

在定义好了Schema之后,我们就需要将其应用到HTTP Server中:

import express from ‘express‘;

import schema from ‘./schema‘;

// new dependencies

import { graphql } from ‘graphql‘;

import bodyParser from ‘body-parser‘;

let app  = express();

let PORT = 3000;

// parse POST body as text

app.use(bodyParser.text({ type: ‘application/graphql‘ }));

app.post(‘/graphql‘, (req, res) => {

  // execute GraphQL!

  graphql(schema, req.body)

  .then((result) => {

    res.send(JSON.stringify(result, null, 2));

  });

});

let server = app.listen(PORT, function () {

  var host = server.address().address;

  var port = server.address().port;

  console.log(‘GraphQL listening at http://%s:%s‘, host, port);

});

所有针对/graphql的查询都会在定义好的Schema下执行,这里我们默认的返回count值,还是使用Curl进行简单的调试可以得到:

$ node ./index.js // restart your server
// in another shell
$ curl -XPOST -H "Content-Type:application/graphql"  -d ‘query RootQueryType { count }‘ http://localhost:3000/graphql
{
 "data": {
   "count": 0
 }
}

Introspect the Server:获取Server定义的Schema信息


实际上,GraphQL Server也可以返回其定义好的Schema信息:

$ curl -XPOST -H ‘Content-Type:application/graphql‘  -d ‘{__schema { queryType { name, fields { name, description} }}}‘ http://localhost:3000/graphql
{
 "data": {
   "__schema": {
     "queryType": {
       "name": "RootQueryType",
       "fields": [
         {
           "name": "count",
           "description": null
         }
       ]
     }
   }
 }
}

其使用的查询实际上就是这个样子:

{
 __schema {
   queryType {
     name, 
     fields {
       name,
       description
     }
   }
 }
}

实际上,我们也可以为每个定义的域添加譬如description, isDeprecated, 以及 deprecationReason这样的描述信息,譬如:

let schema = new GraphQLSchema({

  query: new GraphQLObjectType({

    name: ‘RootQueryType‘,

    fields: {

      count: {

        type: GraphQLInt,

        // add the description

        description: ‘The count!‘,

        resolve: function() {

          return count;

        }

      }

    }

  })

});

那么返回的新的元信息就是:

$ curl -XPOST -H ‘Content-Type:application/graphql‘  -d ‘{__schema { queryType { name, fields { name, description} }}}‘ http://localhost:3000/graphql
{
 "data": {
   "__schema": {
     "queryType": {
       "name": "RootQueryType",
       "fields": [
         {
           "name": "count",
           "description": "The count!"
         }
       ]
     }
   }
 }
}

Add a Mutation:GraphQL中支持增删改


上文中所讲的都是基于GraphQL定义一个查询方式,而GraphQL也是支持对于数据的增删改,这在GraphQL中称为mutations。Mutations也是一个域,其主要是为了指明某个请求打来的Side Effects,因此大部分的语法还是一致的。Mutations也是需要提供一个返回值的,主要是为了返回你改变的值以供验证修改是否成功。

let schema = new GraphQLSchema({

  query

  mutation: new GraphQLObjectType({

    name: ‘RootMutationType‘,

    fields: {

      updateCount: {

        type: GraphQLInt,

        description: ‘Updates the count‘,

        resolve: function() {

          count += 1;

          return count;

        }

      }

    }

  })

});

对应的查询方式就是:

$ curl -XPOST -H ‘Content-Type:application/graphql‘ -d ‘mutation RootMutationType { updateCount }‘ http://localhost:3000/graphql
{
 "data": {
   "updateCount": 1
 }
}

GraphiQL

■欢迎关注 Reboot教育  运维自动化班---9月11(周日)开班

详情点击:http://www.51reboot.com/course/devops/

上课形式:面授班 / 网络直播班

报名QQ:979950755

时间: 2024-12-14 03:37:16

GraphQL入门指南的相关文章

Quartz.NET简介及入门指南

Quartz.NET简介 Quartz.NET是一个功能完备的开源调度系统,从最小的应用到大规模的企业系统皆可适用. Quartz.NET是一个纯净的用C#语言编写的.NET类库,是对非常流行的JAVA开源调度框架 Quartz 的移植. 入门指南 本入门指南包括以下内容: 下载 Quartz.NET 安装 Quartz.NET 根据你的特定项目配置 Quartz 启动一个样例程序 下载和安装 你可以下载 zip 文件或使用 Nuget 程序包.Nuget 程序包只包含 Quartz.NET 运

Java程序员的Golang入门指南(上)

Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如果有人说X语言比Y语言好,两方的支持者经常会激烈地争吵.如果你是某种语言老手,你就是那门语言的"传道者",下意识地会保护它.无论承认与否,你都已被困在一个隧道里,你看到的完全是局限的.<肖申克的救赎>对此有很好的注脚: [Red] These walls are funny.

【翻译Autofac的帮助文档】1.入门指南

[写在前面]尝试做完一件工作之外自我觉得有意义的一件事,那就从翻译Autofac的帮助文档吧. 入门指南 将Autofac集成你的应用程序的步骤通常很简单,一般是: 时刻以IOC(控制反转)的思想来规划你的应用程序 在你的Porject中添加Autofac引用 按照如下步骤设计应用程序的启动环节 创建一个ContainerBuilder 向ContainerBuilder注册组件 通过ContainerBuilder的Build()方法获得Container(后续需用到) 在应用程序运行环节时,

Markdown——入门指南

导语: Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的语法十分简单.常用的标记符号也不超过十个,这种相对于更为复杂的 HTML 标记语言来说,Markdown 可谓是十分轻量的,学习成本也不需要太多,且一旦熟悉这种语法规则,会有一劳永逸的效果. Ulysses for Mac 一,认识 Markdown 在刚才的导语里提到,Markdown 是一种用来写作的轻量级「标记语言」

Win32编程API 基础篇 -- 1.入门指南 根据英文教程翻译

入门指南 本教程是关于什么的 本教程的目的是向你介绍使用win32 API编写程序的基础知识(和通用的写法).使用的语言是C,但大多数C++编译器也能成功编译,事实上,教程中的绝大多数内容都适用于任何可以连接API的语言,包括Java.Assembly和Visual Basic:我不会向你呈现任何跟这些语言相关的代码,这需要你在本教程的指导下自己去完成,有一些人在本API的基础上使用其他语言进行编程取得了相当的成功. 本教程不会教你C语言,也不会告诉你怎样去运行你特定的编译器(Borland C

[转载]TFS入门指南

[原文发表地址] Tutorial: Getting Started with TFS in VS2010 [原文发表时间] Wednesday, October 21, 2009 1:00 PM 本月初,我们发布了TFS新基础配置.该配置为建立支持源码管理,工作项和生成(builds)的TFS版本提供了便利. 这是一个好机会将你在VSS(Visual Source Safe)上的资源迁移到TFS,并且还可以选用一些新的特性.现在VS2010 Beta2的正式版已经发布了,下面是该系统的入门指南

编程入门指南

前言 如今编程成为了一个越来越重要的「技能」:作为设计师,懂一些编程可能会帮你更好地理解自己的工作内容:作为创业者,技术创始人的身份则会让你的很多工作显得更容易.而作为刚想入门的新手,面对眼前海量的信息,或许根本不知道从哪里开始:入门轻松度过初级材料的学习后,发现学习越来越困难,陡峭的学习曲线又让你望而却步:你知道如何在页面上打印输出一些文本行,但是你不知道何时该进行一个真正的有用的项目:你不清楚自己还有哪些不知道的东西,你甚至搞不清下一步该学什么. 这篇文章的内容对此不仅会有一些方向性的建议,

物联网操作系统HelloX开发者入门指南

HelloX开发者入门指南 HelloX是聚焦于物联网领域的操作系统开发项目,可以通过百度搜索"HelloX",获取详细信息.当前开发团队正在进一步招募中,欢迎您的了解和加入.如果您希望加入HelloX的开发团队,建议参照下列步骤进行操作: 1.      首先,请亲手熟悉和操作HelloX操作系统,这是我们开发的核心组件之一,所有其它组件(包括后台组件,终端产品等)都围绕HelloX操作系统展开.这一步很容易操作,请从github(github.com/hellox-project/

STM32F10X入门指南---AD转换

首先,点击下面的链接下载我们需要使用的代码.链接 1.添加必要的文件: 之前我们说过,有三个文件是必须添加的,这三个文件分别是:startup_stm32f10x_xd.s ,stm32f10x_rcc.c ,system_stm32f10x.c.其中,前面的xd是根据你的芯片的容量来选择的.这三个文件都可以在千帆提供的代码中找到.文件路径:Core.rar\Core\STM32\Source\Must . 另外,如果想操作IO口,必须添加千帆的一个库文件DeviceBase.cpp.文件路径: