10. 使用真实数据源
我们已经学习不少关于GraphQL的知识了。在之前的例子中,使用的都是存储在内存中的数据,但在现实开发中,你需要使用的是GraphQL模式后的真实数据源。
这也正是本节课所要完成的目标。我们将选择MongoDB作为数据源,并在课程的最后演示如何集成其他数据源。
现在就开始吧!
准备工作
首先你需要一个MongoDB数据库。 我们建议下载MongoDB并在本地运行它。 如果不能正常下载的话,你可以使用任何提供MongoDB服务的云解决方案
然后,复制GraphQL Sandbox项目到本地:
git clone https://github.com/kadirahq/graphql-blog-schema.git
cd graphql-blog-schema
接着,切换到server-side-schema
分支。
git checkout server-side-schema
最后,安装依赖项并启动它:
npm install
npm start
我们就可以在3000端口访问shandbox项目了,http:// localhost:3000
这版的sandbox与之前的稍有不同。在前几节课的例子中,我们都是在客户端里定义schema,但这次,我们要在服务器端来定义它。
为此,我们将使用express-graphql项目。
模式文件
在本项目中,schema文件位于server / schema.js
中,并且已经定义好了一个authors
的根查询字段和一个名为createAuthor
的mutation。
如果你学习了之前的课程的话,相信对这些东东都不会陌生吧:)
Async / promises
回想之前的例子,我们只是在resolve函数中简单地返回一个值。由于数据都在内存中,所以并不会导致什么问题。但当我们访问一个真实的数据源时,立即返回结果几乎是不可能的,因此必须有异步的处理。
ES2015 promise能帮助我们解决这个问题。我们可以在resolve函数里返回一个promise, 然后GraphQL就能快速有效的处理它们。
访问MongoDB
我们需要一个支持promise的MongoDB驱动程序来访问MongoDb,这本项目中,将使用promised-mongo,它已经安装在我们的repo里了
实现mutation
首先,需要创建一个MongoDB的连接,我们在schema.js
的顶部添加下面的代码:
const mongo = require('promised-mongo');
// You can use any MONGO_URL here, whether it's locally or on cloud.
const db = mongo('mongodb://localhost/mydb');
const authorsCollection = db.collection('authors');
然后实现createAuthor
的处理逻辑。so,我们使用promised-mongo
这个包来提供promise的API。
mutation与mongoDB相关的代码如下:
const Mutation = new GraphQLObjectType({
name: "Mutations",
fields: {
createAuthor: {
type: Author,
args: {
_id: {type: new GraphQLNonNull(GraphQLString)},
name: {type: new GraphQLNonNull(GraphQLString)},
twitterHandle: {type: GraphQLString}
},
resolve: function(rootValue, args) {
let author = Object.assign({}, args);
return authorsCollection.insert(author)
.then(_ => author);
}
}
}
});
现在你可以按照下面的方法在GraphQL Sandbox里调用mutation了
mutation _ {
createAuthor(
_id: "john",
name: "John Carter"
) {
name
}
}
代码执行后,你会得到以下结果:
{
"data": {
"createAuthor": {
"name": "John Carter"
}
}
}
任务时间!
试试再次调用上面的mutation。 会得到什么结果咧?
- It was successful as usual.
- Got an empty response.
- Got an error stating that the insert had been forbidden.
- Got an error citing a duplicate key error.
实现根查询字段
通过上一节课,我们完成了添加新作者的方法,现在就让我们来实现authors
查询字段。
这将是根查询类与MongoDB的集成哦。
const Query = new GraphQLObjectType({
name: "RootQuery",
fields: {
authors: {
type: new GraphQLList(Author),
resolve: function() {
return authorsCollection.find().toArray();
}
}
}
});
然后,你就可以在GraphQL SandBox中检索作者了:
{
authors {
name
}
}
ok的话,你能够从Mongo数据库中获取到所有作者的名字。
运行似乎很顺利嘛,可是却有一个小问题哦。看看你是否发现它了:
- It can’t do field filtering in MongoDB level.
- It can't limit the result.
- It can't sort the result.
- There is no such issue.
即使没有排序和限制功能,我们仍然可以通过传递一些参数来简单的实现这些效果。 但是如何过滤出那些在查询中提及的字段呢?
这正是本节课我们要完成的内容。
字段过滤
虽然没有直接的方法从resolve函数检索字段。 但仍有解决办法。看看修改后的resolve函数吧:
const Query = new GraphQLObjectType({
name: "Queries",
fields: {
authors: {
type: new GraphQLList(Author),
resolve: function(rootValue, args, info) {
let fields = {};
let fieldASTs = info.fieldASTs;
fieldASTs[0].selectionSet.selections.map(function(selection) {
fields[selection.name.value] = 1;
});
return authorsCollection.find({}, fields).toArray();
}
}
}
});
我们使用了传给resolve函数的第三个参数:info。它有该查询解析后的AST数据。通过它,我们就可以检索字段啦,如上面的代码所示。
不止于此
即使仅使用promised-mongo
,我们也可以很容易地将Mongo数据库与GraphQL模式集成。当然,这只是方法之一,还有好几种方式能够将现有数据源集成到GraphQL中,让我来告诉你它们中的一些吧:
- graffiti - 使用Mongoose模式与GraphQL集成
- graphql-sequelize - 使用Sequelize ORM模式与GraphQL集成
- graphql-bookshelf - 使用BookshelfJS模型集成。
这仅仅是个开始。 将来会有有越来越多的方法实现GraphQL与不同类型数据源的集成。