如何用Node.js和Express创建一个GraphQL服务器

204 阅读5分钟

首先创建一个新的Node.js项目,如果你还没有设置好的话。

这个命令创建了我们需要的package.json 文件,以便与npm

安装npm包express,graphqlexpress-graphql:

npm install express graphql express-graphql

创建一个app.js 文件,让我们从初始化Express服务器开始。

const express = require('express')

const app = express()

app.listen(3000, () => {
  console.log('App listening on port 3000')
})

现在我们添加express-graphql 。这是一个中间件,我们只将它应用于一个路由,即/graphql 路由。

const express = require('express')
const graphqlHTTP = require('express-graphql')

const app = express()

app.use('/graphql', graphqlHTTP())

app.listen(3000, () => {
  console.log('App listening on port 3000')
})

我们必须传递一个包含schema 属性的对象,它必须包含一个模式定义

我们必须先定义一个模式!

创建一个schema.js 文件,在那里我们首先需要graphql ,然后使用对象去结构化的语法,得到我们很快需要使用的GraphQLSchemaGraphQLObjectTypeGraphQLString 对象。

const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = graphql

然后我们通过初始化一个新的GraphQLSchema 实例来定义模式值,传递一个包含query 属性的对象。这个属性是一个GraphQLObjectType 对象的实例。

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    //...
  }),
})

module.exports = schema

在这个新对象中,我们必须指定一个name ,和一个fields 参数。这最后一个属性是一个包含一组属性的对象,我们模式的每个字段都有一个。在这个例子中,我们设置了一个hello 字段。

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      hello: {
        type: GraphQLString,
        resolve() {
          return 'world'
        },
      },
    },
  }),
})

resolve() 方法返回字符串world ,这意味着当我们将询问hello 字段时,我们会得到这个字符串。

这里是完整的schema.js 文件内容。

const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = graphql

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      hello: {
        type: GraphQLString,
        resolve() {
          return 'world'
        },
      },
    },
  }),
})

module.exports = schema

现在让我们回到我们的app.js 文件。

这就是我们的内容。

const express = require('express')
const graphqlHTTP = require('express-graphql')

const app = express()

app.use('/graphql', graphqlHTTP())

app.listen(3000, () => {
  console.log('App listening on port 3000')
})

我们现在需要schema.js 文件。

const schema = require('./schema.js')

并将其添加到我们传递给graphqlHTTP() 构造函数的一个对象中。

app.use(
  '/graphql',
  graphqlHTTP({
    schema: schema,
  })
)

好了!

我们现在可以测试一下,看看它是否有效。我们可以使用GraphiQL,一个测试GraphQL API的伟大工具。

它已经安装好了,为了启用它,我们需要向graphqlHTTP 构造函数传递另一个属性。

app.use(
  '/graphql',
  graphqlHTTP({
    schema: schema,
    graphiql: true,
  })
)

现在,在你运行node app.js ,用浏览器访问http://localhost:3000/graphql URL之后,你会看到GraphiQL在工作。

你可以测试第一个API调用,通过这个查询。

这就是结果。

现在让我们建立一个更复杂的模式。

一个有嵌套类型的模式。

我想到的一个例子是一篇博客文章。

一篇博文有一个标题,一个描述,也有一个作者。作者有一个名字。

让我们来弄清楚这个问题。

首先,我们添加帖子和作者的集合。

const posts = [  {    title: 'First post',    description: 'Content of the first post',    author: 'Flavio',  },  {    title: 'Second post',    description: 'Content of the second post',    author: 'Roger',  },]

const authors = {
  Flavio: {
    name: 'Flavio',
    age: 36,
  },
  Roger: {
    name: 'Roger',
    age: 7,
  },
}

这就是我们要获取数据的地方。

接下来,我们定义3个GraphQLObjectType 实例。

  • authorType,它定义了作者的数据
  • postType,它定义了帖子的数据
  • queryType,主要的一个

让我们从作者开始。一个作者有一个名字和一个年龄。

我们使用GraphQLInt ,我们必须把这个类型添加到require中。

const { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInt } = graphql

//...

const authorType = new GraphQLObjectType({
  name: 'Author',
  fields: {
    name: {
      type: GraphQLString,
    },
    age: {
      type: GraphQLInt,
    },
  },
})

接下来是postType 。一个帖子有一个标题,一个描述(都是字符串)和一个作者。一个作者的类型是authorType ,我们刚刚定义了这个类型,它有一个解析器。

我们从source 参数中获得作者的名字,这是传递给帖子对象的参数,我们在此基础上查找作者数据。我们返回它。

const postType = new GraphQLObjectType({
  name: 'Post',
  fields: {
    title: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    author: {
      type: authorType,
      resolve: (source, params) => {
        return authors[source.author]
      },
    },
  },
})

注意,一个解析器函数可以是异步的,所以你可以使用async/await来从数据库或网络上查找资源。

接下来是queryType,这是我们要添加到模式中的根类型。在这里,我们定义了两个字段。

  • post 一个单一的博客文章,由一个id标识
  • posts 帖子的列表

这两个字段都有一个解析函数来查找posts 阵列中的数据。

const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    post: {
      type: postType,
      args: {
        id: { type: GraphQLInt },
      },
      resolve: (source, { id }) => {
        return posts[id]
      },
    },
    posts: {
      type: new GraphQLList(postType),
      resolve: () => {
        return posts
      },
    },
  },
})

注意新的GraphQLList 类型,我们用它来包装postType ,意味着它是一个postType 对象的列表。我们必须在上面要求它。

const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLList,
  GraphQLInt,
} = graphql

就这样了。我们需要把它添加到我们的schema ,我们就可以了。

const schema = new GraphQLSchema({
  query: queryType,
})

这里是完整的代码。

const graphql = require('graphql')
const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLList,
  GraphQLInt,
} = graphql

const posts = [  {    title: 'First post',    description: 'Content of the first post',    author: 'Flavio',  },  {    title: 'Second post',    description: 'Content of the second post',    author: 'Roger',  },]

const authors = {
  Flavio: {
    name: 'Flavio',
    age: 36,
  },
  Roger: {
    name: 'Roger',
    age: 7,
  },
}

const authorType = new GraphQLObjectType({
  name: 'Author',
  fields: {
    name: {
      type: GraphQLString,
    },
    age: {
      type: GraphQLInt,
    },
  },
})

const postType = new GraphQLObjectType({
  name: 'Post',
  fields: {
    title: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    author: {
      type: authorType,
      resolve: (source, params) => {
        return authors[source.author]
      },
    },
  },
})

const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    post: {
      type: postType,
      args: {
        id: { type: GraphQLInt },
      },
      resolve: (source, { id }) => {
        return posts[id]
      },
    },
    posts: {
      type: new GraphQLList(postType),
      resolve: () => {
        return posts
      },
    },
  },
})

const schema = new GraphQLSchema({
  query: queryType,
})

module.exports = schema

请看Glitch上的完整代码