GraphQLのサンプルに書き加えてみた
できたこと
独自のItemというデータ構造を追加してそれを作成・取得することができるようになった。
データ定義をしておくとバリテーションをしてくれることがわかった。 クライアント側で欲しいフィールドを調整したい場合は便利そうな気がした。
独自実装を追加していく
前回起動まで成功した https://github.com/RisingStack/graphql-serverをローカルにクローンしてきてこのクライアントとサーバーをいじっていきます。
Nodejs+MongoDBという環境で実装されていて、今回は
- mongoose(MongoDBライブラリ)のモデルクラスの定義
- GraphQLのスキーマを定義
- クライアント側コードを記述。作成と取得クエリを投げるようにする
という手順で作業を進めます。
mongoose(MongoDBライブラリ)のモデルクラスの定義
src/server/user.jsがmongooseのモデルクラスの定義なので、これをコピペしてsrc/server/item.jsを作ります。以下のようなコードになりました。
import mongoose from 'mongoose'; var ItemSchema = new mongoose.Schema({ name: { type: String } }); var Item = mongoose.model('Item', ItemSchema); export default Item;
GraphQLのスキーマを定義
src/server/schema.jsを編集していきます。
import Item from './item';
を追加して先ほど書いたitem.jsを読み込みます。
GraphQLスキーマ向けにモデル定義をします。mongooseの方でもしてるので一緒にできると良いなと思います。
var itemType = new GraphQLObjectType({ name: 'Item', description: 'Item creator', fields: () => ({ id: { type: new GraphQLNonNull(GraphQLString), description: 'The id of the item.', }, name: { type: GraphQLString, description: 'The name of the item.', }, }) });
GraphQLSchemaのqueryには取得クエリ、mutationには作成・更新・削除などの変更を加えるクエリを実装します。 詳しくは仕様を読むと良いです。
取得クエリ(getItem)を定義
query: new GraphQLObjectType({ name: 'RootQueryType', fields: { getItem: { type: itemType, args: { name: { name: 'name', type: new GraphQLNonNull(GraphQLString) } }, resolve: (root, {name}, source, fieldASTs) => { return Item.findOne({name: name}); } }, } }
次に作成クエリ(createItem)を定義します。
mutation: new GraphQLObjectType({ name: 'Mutation', fields: { createItem: { type: itemType, args: { name: { name: 'name', type: GraphQLString } }, resolve: (obj, {name}, source, fieldASTs) => co(function *() { var item = new Item(); item.name = name; return yield item.save(); }) }, } }),
最終的にsrc/server/schema.jsは以下のようになりました。
import { GraphQLObjectType, GraphQLNonNull, GraphQLSchema, GraphQLString, GraphQLList } from 'graphql/type'; import co from 'co'; import User from './user'; import Item from './item'; /** * generate projection object for mongoose * @param {Object} fieldASTs * @return {Project} */ function getProjection (fieldASTs) { return fieldASTs.selectionSet.selections.reduce((projections, selection) => { projections[selection.name.value] = 1; return projections; }, {}); } var userType = new GraphQLObjectType({ name: 'User', description: 'User creator', fields: () => ({ id: { type: new GraphQLNonNull(GraphQLString), description: 'The id of the user.', }, name: { type: GraphQLString, description: 'The name of the user.', }, friends: { type: new GraphQLList(userType), description: 'The friends of the user, or an empty list if they have none.', resolve: (user, params, source, fieldASTs) => { var projections = getProjection(fieldASTs); return User.find({ _id: { // to make it easily testable $in: user.friends.map((id) => id.toString()) } }, projections); }, } }) }); var itemType = new GraphQLObjectType({ name: 'Item', description: 'Item creator', fields: () => ({ id: { type: new GraphQLNonNull(GraphQLString), description: 'The id of the item.', }, name: { type: GraphQLString, description: 'The name of the item.', }, }) }); var schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQueryType', fields: { hello: { type: GraphQLString, resolve: function() { return 'world'; } }, user: { type: userType, args: { id: { name: 'id', type: new GraphQLNonNull(GraphQLString) } }, resolve: (root, {id}, source, fieldASTs) => { var projections = getProjection(fieldASTs); return User.findById(id, projections); } }, getItem: { type: itemType, args: { name: { name: 'name', type: new GraphQLNonNull(GraphQLString) } }, resolve: (root, {name}, source, fieldASTs) => { return Item.findOne({name: name}); } }, } }), // mutation mutation: new GraphQLObjectType({ name: 'Mutation', fields: { createUser: { type: userType, args: { name: { name: 'name', type: GraphQLString } }, resolve: (obj, {name}, source, fieldASTs) => co(function *() { var projections = getProjection(fieldASTs); var user = new User(); user.name = name; return yield user.save(); }) }, deleteUser: { type: userType, args: { id: { name: 'id', type: new GraphQLNonNull(GraphQLString) } }, resolve: (obj, {id}, source, fieldASTs) => co(function *() { var projections = getProjection(fieldASTs); console.log(id); return yield User.findOneAndRemove({_id: id}); }) }, updateUser: { type: userType, args: { id: { name: 'id', type: new GraphQLNonNull(GraphQLString) }, name: { name: 'name', type: GraphQLString } }, resolve: (obj, {id, name}, source, fieldASTs) => co(function *() { var projections = getProjection(fieldASTs); yield User.update({ _id: id }, { $set: { name: name } }); return yield User.findById(id, projections); }) }, createItem: { type: itemType, args: { name: { name: 'name', type: GraphQLString } }, resolve: (obj, {name}, source, fieldASTs) => co(function *() { var projections = getProjection(fieldASTs); var item = new Item(); item.name = name; return yield item.save(); }) }, } }), }); export var getProjection; export default schema;
クライアント側コードを記述。作成と取得クエリを投げるようにする
作成クエリを投げるコードを書くのでsrc/client/mutation.jsを編集します。
Itemデータを作成するために先ほど実装したcreateItemを使います。hogehogehogeというItemを作成します。
var itemName = 'hogehogehoge' request .post('http://localhost:3000/data') .send({ query: ` mutation M($name: String!) { createItem(name: $name) { name } } `, params: { name: itemName } }) .end(function (err, res) { debug(err || res.body); debug('created', res.body); });
src/client/mutation.jsは以下のようになりました。最初からあったコードはデバッグの都合で除去しました。
import request from 'superagent'; import Debug from 'debug'; var debug = new Debug('client:mutation'); var itemName = 'hogehogehoge' request .post('http://localhost:3000/data') .send({ query: ` mutation M($name: String!) { createItem(name: $name) { name } } `, params: { name: itemName } }) .end(function (err, res) { debug(err || res.body); debug('created', res.body); });
取得クエリを投げるコードを書きます。src/client/query.jsを以下のようなコードを追加します。 (var name = 'hogehogehoge'としていたらうまく動かなかったです。変数名とフィールド名がぶつかるとまずい?)
var itemName = 'hogehogehoge' request .get('http://localhost:3000/data') .query({ query: `{ getItem(name: "${itemName}") { name } }` }) .end(function (err, res) { debug('items', res.body); });
ということで、src/client/query.jsは以下のようになりました。最初からあったコードはデバッグの都合で除去しました。
import request from 'superagent'; import Debug from 'debug'; var debug = new Debug('client:query'); var itemName = 'hogehogehoge' request .get('http://localhost:3000/data') .query({ query: `{ getItem(name: "${itemName}") { name } }` }) .end(function (err, res) { debug('items', res.body); });
上記のようにコードを編集していったら、前回同様に
npm start
としてサーバーを起動して、別のscreenで
npm run client
とクライアントを起動すると、無事データの作成と取得が確認できます。