准备工作:
本章节示例项目源码位于: https://github.com/willin/start-grahql-server
安装各类依赖,养成良好习惯,第一步从eslint起.
!> 另外, 不建议安装Babel来转译服务器端的代码,因为最新的 Node.js 版本已经支持了很多 ES7 的新特性.
graphql-tools 定义结构(Schema): http://dev.apollodata.com/tools/graphql-tools/generate-schema.html
使用 graphql-tools 自动生成结构:
const typeDefs = `
type Author {
  id: Int
  firstName: String
  lastName: String
  posts: [Post]
}
type Post {
  id: Int
  title: String
  text: String
  views: Int
  author: Author
}
type Query {
 author(firstName: String, lastName: String): Author
 post(title: String): Post
 getFortuneCookie: String
}
schema {
 query: Query
}
`;
如果不使用该工具自动生成, 而是想要自己定义完整的数据结构, 可以参考: https://graphql.js.cool/learn/schema/
也可以在后面进阶的章节中了解: MySQL 向 GraphQL 迁移
graphql-serve: https://github.com/apollographql/graphql-server
本文中分别使用 express 和 koa 做了两个服务器.
// express
const express = require('express');
const { graphqlExpress, graphiqlExpress } = require('graphql-server-express');
const bodyParser = require('body-parser');
const schema = require('./schema');
const GRAPHQL_PORT = 3000;
const graphQLServer = express();
graphQLServer.use('/graphql', bodyParser.json(), graphqlExpress({ schema }));
graphQLServer.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
graphQLServer.listen(GRAPHQL_PORT, () => console.log(
  `GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}/graphql`
));
// koa
const Koa = require('koa');
const KoaRouter = require('koa-router');
const koaBody = require('koa-bodyparser');
const { graphqlKoa, graphiqlKoa } = require('graphql-server-koa');
const schema = require('./schema');
const app = new Koa();
const router = new KoaRouter();
const PORT = 3000;
// koaBody is needed just for POST.
app.use(koaBody());
router.all('/graphql', graphqlKoa({ schema }));
router.all('/graphiql', graphiqlKoa({ endpointURL: '/graphql' }));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT);
使用 sequelize
const db = new Sequelize('blog', 'root', 'root', {
  dialect: 'mysql',
  host: 'localhost'
});
const AuthorModel = db.define('author', {
  firstName: { type: Sequelize.STRING },
  lastName: { type: Sequelize.STRING }
});
const PostModel = db.define('post', {
  title: { type: Sequelize.STRING },
  text: { type: Sequelize.STRING }
});
AuthorModel.hasMany(PostModel);
PostModel.belongsTo(AuthorModel);
// create mock data with a seed, so we always get the same
casual.seed(123);
db.sync({ force: true }).then(() => {
  _.times(10, () => AuthorModel.create({
    firstName: casual.first_name,
    lastName: casual.last_name
  }).then(author => author.createPost({
    title: `A post by ${author.firstName}`,
    text: casual.sentences(3)
  })));
});
const Author = db.models.author;
const Post = db.models.post;
使用 mongoose
// somewhere in the middle:
Mongoose.connect('mongodb://localhost/views');
const ViewSchema = Mongoose.Schema({
  postId: Number,
  views: Number
});
const View = Mongoose.model('views', ViewSchema);
const FortuneCookie = {
  getOne() {
    return fetch('http://fortunecookieapi.herokuapp.com/v1/cookie')
      .then(res => res.json())
      .then(res => res[0].fortune.message);
  }
};
GraphQL后边可以连接各种持久化存储,甚至RESTful远程资源.
const { Author, View, FortuneCookie } = require('./connectors');
const resolvers = {
  Query: {
    author(_, args) {
      // MySQL
      return Author.find({ where: args });
    },
    getFortuneCookie() {
      // 远程REST服务
      return FortuneCookie.getOne();
    }
  },
  Author: {
    posts(author) {
      return author.getPosts();
    }
  },
  Post: {
    author(post) {
      return post.getAuthor();
    },
    views(post) {
      // MongoDB
      return View.findOne({ postId: post.id })
        .then(view => view.views);
    }
  }
};
module.exports = resolvers;