GraphQL简单实践
之前就简单接触过GraphQL,但一直没怎么上手实践。最近想用GraphQL来做后端的API层,正好可以真正地用一用了。
以下所有代码均为使用GraphQL的JavaScript的SDK。
Server side
GraphQL官方提供了一个express
插件可以很方便地在express的基础上构建GraphQL API:
var express = require('express');
var graphqlHTTP = require('express-graphql');
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
其中,graphqlHTTP
接收了三个参数,graphiql: true
表示会提供一个方便查询的GraphQL的前端界面,否则只有纯后端的API。另外两个参数后面会详细讲一讲。
GraphQL Schema
GraphQL自创了一套schema的语法,通过graphql.buildSchema
函数可以将这套语法进行解析并生成JavaScript中对应的数据结构:
var {buildSchema} = require('graphql');
var schema = buildSchema(`
input MessageInput {
content: String
author: String
}
type Message {
id: ID!
content: String
author: String
}
type Query {
getMessage(id: ID!): Message
}
type Mutation {
createMessage(input: MessageInput): Message
updateMessage(id: ID!, input: MessageInput): Message
}
`);
当然,我们也可以不使用这套语法,而直接使用GraphQL提供了内置类型来生成schema,下面的代码可以达到一模一样的效果:
var {GraphQLSchema, GraphQLObjectType, GraphQLInputObjectType, GraphQLString, GraphQLID} = require('graphql');
const MessageInput = new GraphQLInputObjectType({
name: 'MessageInput',
fields: {
content: {type: GraphQLString},
author: {type: GraphQLString},
},
});
const Msg = new GraphQLObjectType({
name: 'Msg',
fields: {
id: {type: GraphQLID},
content: {type: GraphQLString},
author: {type: GraphQLString},
},
});
const Query = new GraphQLObjectType({
name: 'Query',
fields: {
getMessage: {
type: Msg,
args: {
id: {type: GraphQLID},
},
}
}
});
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
createMessage: {
type: Msg,
args: {
input: {type: MessageInput},
},
},
updateMessage: {
type: Msg,
args: {
id: {type: GraphQLID},
input: {type: MessageInput},
},
},
},
});
var schema = new GraphQLSchema({
MessageInput: MessageInput,
Message: Msg,
query: Query,
mutation: Mutation,
});
以上,可以看到使用GraphQL提供的语法会更加简洁一些。
此外,需要注意的是,GraphQL的schema必须包含名为query
的field(用于查询用),除了query
外,还有一个特殊的field叫做mutation
(用于增、删、改),后面的client端的查询语法也可以看到有两个关键字与之对应。除了这些特殊的field外,其他的field是用于定义一些复杂类型的。
GraphQL rootValue
这个参数其实是和schema配合起来看的,比如上面例子中的schema中声明了三个函数getMessage
、createMessage
、updateMessage
,这些函数的定义就放在rootValue中:
var root = {
getMessage: function ({id}) {
if (!fakeDatabase[id]) {
throw new Error('no message exists with id ' + id);
}
return new Message(id, fakeDatabase[id]);
},
createMessage: function ({input}) {
// Create a random id for our "database".
var id = require('crypto').randomBytes(10).toString('hex');
fakeDatabase[id] = input;
return new Message(id, input);
},
updateMessage: function ({id, input}) {
if (!fakeDatabase[id]) {
throw new Error('no message exists with id ' + id);
}
// This replaces all old data, but some apps might want partial update.
fakeDatabase[id] = input;
return new Message(id, input);
},
};
GraphQL通过名称来对他们进行匹配。
除此之外,如果你的schema定义是使用的GraphQL的内置类型来创建的,那么你还可以通过每一个field的resolve
属性来定义对应的函数(注意如果通过resolve
定义了函数,那么rootValue中对应的函数就不会起作用了)。以getMessage
函数为例:
const Query = new GraphQLObjectType({
name: 'Query',
fields: {
getMessage: {
type: Msg,
args: {
id: {type: GraphQLID},
},
resolve(args, context, info) {
// If the resolve function is provided here, the corresponding function in `rootValue` will not be called
const id = context.id;
if (!fakeDatabase[id]) {
throw new Error('no message exists with id ' + id);
}
return new Message(id, fakeDatabase[id]);
}
}
}
});
Client side
从客户端对GraphQL的API进行查询其实就是往GraphQL的API上发送一个POST请求,请求内容为一个Json格式的字符串,格式也同样为GraphQL所定义的一套语法,且要满足对应的schema定义。
基本格式为:
{
"query": xxx,
"variables": xxx
}
其中,query为满足GraphQL定义语法的查询语言,variables为query中用到的变量(如果有的话)。
对应上面server端的一个简单的例子如下:
{
"query": "mutation CreateMessage($input: MessageInput) {\n createMessage(input: $input) {\n id\n }\n}",
"variables": {
"input": {
"author": "andy",
"content": "hope is a good thing"
}
}
}
*上述所有完整代码可查看graphql-demo。
Comments