GraphQL初体验

什么是GraphQL

GraphQL是Facebook开发的一个应用层的数据查询语言。通过GraphQL,客户端可以从服务端的数据集中轻松获得一个自定义结构的数据。

wigq.png

按照官网上的例子,如果我们的查询语句如:

{
  user(id: 3500401) {
    id,
    name,
    isViewerFriend,
    profilePicture(size: 50)  {
      uri,
      width,
      height
    }
  }
}

那么,GraphQL服务器将返回:

{
  "user" : {
    "id": 3500401,
    "name": "Jing Chen",
    "isViewerFriend": true,
    "profilePicture": {
      "uri": "http://someurl.cdn/pic.jpg",
      "width": 50,
      "height": 50
    }
  }
}

需要说明的是,有很多语言已经实现了GraphQL。具体见:awesome-graphql

Getting Started

还是让我们先参照官网上的例子,使用Node.js实现GraphQL服务器。

Step 1

  • 创建工程目录hello-graphql
  • 初始化项目,以及引入所需的包

    npm init -f
    npm install graphql express express-graphql --save
    npm install babel-cli babel-preset-es2015 --save-dev
    npm install nodemon --save-dev
    

Step 2

创建data目录,并在该目录下创建data.json文件,并输入以下内容。

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

Step 3

创建server.js文件,并输入以下代码内容。

import express from 'express';
var http = require('http');

const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello!');
});


app.set('port', port);

var server = http.createServer(app);
server.listen(port);
server.on('listening', onListening);

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
  console.log('Listening on ' + bind);
}

命令行运行nodemon --exec babel-node --presets=es2015 -- server.js,在浏览器输入http://localhost:3000。显示Hello!,说明你的服务express服务已经OK了。

Step 4

创建文件夹/src/schema,并创建文件index.js。将在index.js文件中编写GraphQL Schema。Schema是GraphQL请求的入口,用户请求的GraphQL将会对应到具体的Schema。

import {
  GraphQLList,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
  GraphQLInt,
  GraphQLFloat,
  GraphQLEnumType,
  GraphQLNonNull
} from 'graphql';

const data = require('../../data/data.json');   //我们要用的模拟数据


const User = new GraphQLObjectType({
  name: 'User',
  description: 'User对象',
  fields: {
    id: { type: GraphQLString },
    name: { type: GraphQLString },
  }
});

const Query = new GraphQLObjectType({
  name: 'Query',
  fields: {
    user: {
      type: User,
      args: {
          id: { type: GraphQLString }
      },
      resolve: function (_, args) {
          return data[args.id];
      }
    }
  }
});

const Schema = new GraphQLSchema({
  query: Query
});

export default Schema;

Step 5

修改server.js文件,连接schema。将server.js文件修改为以下内容:

import express from 'express';
var http = require('http');
var expressGraphql = require('express-graphql');
// var Schema = require('./src/schema');
import Schema from './src/schema';

const app = express();
const port = 3000;

app.set('port', port);


app.use('/', expressGraphql({
  schema: Schema,
  graphiql: true
}));


var server = http.createServer(app);
server.listen(port);
server.on('listening', onListening);

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
  console.log('Listening on ' + bind);
}

Step 6

在浏览器中可以查看到如下界面。
result.png

在左边的空白处输入:

{
  user(id: "1") {
    name
  }
}

点击“运行”按钮。右边会给出结果

{
  "data": {
    "user": {
      "name": "Dan"
    }
  }
}

期间遇到的一个问题

注意到“Step 5”的代码中有一行被注释掉的代码:// var Schema = require('./src/schema');。本来我是这样写的,结果在运行的时候报错了,提示:

error.jpg

然后在graphiql的issues中找到了解决的答案,也就有了import Schema from './src/schema'

总结

到现在为止,我们已经搭好了简单的GraphQL服务器。此次的初体验只是我们将要用到GraphQL的预研,如果在生产环境中使用的话,肯定还会遇到许许多多新的问题。不过它强大的类型系统,它提供的抽象层可以让我们灵活地提供不同的数据支持还是让我印象深刻。

示例代码已上传GitHub:hello-graphql


参考资料: