GraphQL的JavaScript實現(xiàn)graphql-js應用舉例
系列文章:
- GraphQL 核心概念
- graphql-js 淺嘗(本文)
常言道,實踐是檢驗真理的唯一標準。
上一篇文章講了 GraphQL 的核心概念,所提到的一些例子都是理論化的,并沒有實際代碼做支撐,就好像在畫一個大餅,總是讓人不那么信服。
它真的有那么神奇嗎?那就同我一起看下去,用事實說話。
之前那篇文章一直有提到 GraphQL 是一個概念,每個語言可以有自己實現(xiàn)它的方式。因為,我是搞前端的,對 JavaScript 比較熟悉,所以,這里就用 graphql-js(GraphQL 的 JavaScript 實現(xiàn))來舉例。
Hello World
遵循傳統(tǒng),***個例子必須是 Hello World。
首先,安裝就不用多說了。
- npm install graphql-js --save
那這個例子該怎么設計哪?假設,查詢一個 hello 字符串,就返回一個 world 字符串,很明顯 type 的結構就該是這樣
- type HelloWorld {
- hello: String
- }
如何實現(xiàn)這個 HelloWorld 類型哪?graphql-js 已經(jīng)定義好了基礎類,我們直接調(diào)用就行。那么,這個 type 實現(xiàn)起來也就非常簡單了
- import {
- GraphQLString,
- GraphQLObjectType,
- } from 'graphql';
- const HelloWorldType = new GraphQLObjectType({
- name: 'HelloWorldType',
- fields: () => ({
- hello: {
- type: GraphQLString,
- }
- })
- });
簡單分析一下上面的代碼,可以看到 HelloWorldType 是一個 GraphQLObjectType 的實例,它包含一個 fields 是 hello,這個 hello 所對應的返回類型是字符串。
那如何返回 world 字符串?那就給它個 resolve 方法
- const HelloWorldType = new GraphQLObjectType({
- name: 'HelloWorldType',
- fields: () => ({
- hello: {
- type: GraphQLString,
- resolve() {
- return 'world';
- },
- }
- })
- });
這樣類型就定義好了,還記不記得上篇文章提到的類型定義完成后該怎么辦?
對,創(chuàng)建查詢的 schema。
- import {
- GraphQLString,
- GraphQLObjectType,
- GraphQLSchema,
- } from 'graphql';
- const HelloWorldType = new GraphQLObjectType({
- name: 'HelloWorldType',
- fields: {
- hello: {
- type: GraphQLString,
- resolve() {
- return 'world';
- },
- }
- }
- });
- const schema = new GraphQLSchema({
- query: HelloWorldType
- });
schema 設置好了,是不是想查詢看看哪?
東風當然是服務器啦。GraphQL 官方提供 express-graphql 這個中間件來支持基于 GraphQL 的查詢,所以,這里選用 Express 作為服務器。
安裝就不再重復了,只需將剛剛建立的 schema 添加到 express 的中間件中就可以了。
- const app = express();
- app
- .use('/graphql', graphqlHTTP({ schema, pretty: true }))
- .listen(3000, () => {
- console.log('GraphQL server running on http://localhost:3000/graphql');
- });
當當當當~完成,去 Postman 里查詢 http://localhost:3000/graphql?query={hello} 看看吧。
Blog System
在上一篇文章里,我們設計了一個博客的查詢 schema,這次我們就來動手實現(xiàn)它。(下面就開始講例子啦,不愿聽我嘮叨的可以直接看代碼)
前面 HelloWorld 的例子講的比較詳細,現(xiàn)在大家熟悉了語法,接下來的案例就會過得快一些。
首先是 PostType,這里對 Posttype 做了一點小修改,給幾個字段添加了不能為空的設計。
- /**
- * type Post {
- * id: ID!,
- * name: String!,
- * createDate: String!,
- * title: String!,
- * subtitle: String,
- * content: String,
- * tags: [Tag]
- * }
- */
- const Post = new GraphQLObjectType({
- name: 'PostType',
- fields: () => ({
- id: {
- type: new GraphQLNonNull(GraphQLID)
- },
- name: {
- type: new GraphQLNonNull(GraphQLString)
- },
- createDate: {
- type: new GraphQLNonNull(GraphQLString)
- },
- title: {
- type: new GraphQLNonNull(GraphQLString)
- },
- subtitle: {
- type: GraphQLString
- },
- content: {
- type: GraphQLString
- },
- tags: {
- type: new GraphQLList(TagType),
- resolve: post => post.tags.map(tagName => getTagByName(tagName))
- }
- })
- });
然后是另一個主要的 type: Tag type。
- /**
- * type Tag {
- * id: ID!,
- * name: String!,
- * label: String!,
- * createDate: String!,
- * posts: [Post]
- * }
- */
- const Tag = new GraphQLObjectType({
- name: 'TagType',
- fields: () => ({
- id: {
- type: new GraphQLNonNull(GraphQLID)
- },
- name: {
- type: new GraphQLNonNull(GraphQLString)
- },
- label: {
- type: new GraphQLNonNull(GraphQLString)
- },
- createDate: {
- type: new GraphQLNonNull(GraphQLString)
- },
- posts: {
- type: new GraphQLList(PostType),
- resolve: tag => getPostsList().filter(post => ~post.tags.indexOf(tag.name))
- }
- })
- });
兩個主要的類型已經(jīng)定義好了,把它們倆整合起來就是博客類型了。
- /**
- * type Blog {
- * post: Post, // 查詢一篇文章
- * posts: [Post], // 查詢一組文章,用于博客首頁
- * tag: Tag, // 查詢一個標簽
- * tags: [Tag], // 查詢所有標簽,用于博客標簽頁
- * }
- */
- const BlogType = new GraphQLObjectType({
- name: 'BlogType',
- fields: () => ({
- post: {
- type: PostType,
- args: {
- name: {
- type: GraphQLString
- }
- },
- resolve: (blog, { name }) => getPostByName(name),
- },
- posts: {
- type: new GraphQLList(PostType),
- resolve: () => getPostsList(),
- },
- tag: {
- type: TagType,
- args: {
- name: {
- type: GraphQLString
- }
- },
- resolve: (blog, { name }) => getTagByName(name),
- },
- tags: {
- type: new GraphQLList(TagType),
- resolve: () => getTagsList(),
- }
- })
- });
這里有一個新東西,就是 arg 字段,用來獲取查詢參數(shù),如果在沒有設置過 arg 字段的屬性上添加變量進行查詢,graphql-js 的驗證系統(tǒng)會報錯。
***,將之前的 helloworld 類型稍微修飾一下,獨立出來,然后和 blog type 整合到一起成為根查詢類。
- const queryType = new GraphQLObjectType({
- name: 'RootQueryType',
- fields: () => ({
- hello: WorldType,
- blog: {
- type: BlogType,
- resolve: () => ({})
- },
- })
- });
- const schema = new GraphQLSchema({
- query: queryType
- });
OK。這樣整個 Demo 就完成了(查看源碼戳這里),快去 Postman 試試各種查詢,體驗 GraphQL 的神奇吧。(不知道怎么寫查詢語句的就看上一篇吧)
***
如果,你不喜歡 GET 方法或查詢字符串過長,express-graphql 也支持 POST 方法,服務器會先查看請求的 URL 中是否包含查詢字符串,如果不包含就會去 request body 中獲取,只需在 request header 中將 Content-Type 設置為 application/graphql 就可以了。
全文一直在說查詢,或許你會疑惑,那我修改怎么做哪?graphql 中的修改稱之為 mutation。mutation 可以定義自己的接口解析類,它在 graphql 的 schema 中是一個可選項,其他的和查詢并無兩樣,只是***在 resolve 方法中的處理方式不同而已。
- const schema = new GraphQLSchema({
- query: queryType,
- mutation: mutationType
- });
***的***提一句,nodemon 很好用,誰用誰知道。