標籤:

graphql-js 淺嘗

A JavaScript implementation for GraphQL

系列文章:

  1. GraphQL 核心概念
  2. graphql-js 淺嘗(本文)

常言道,實踐是檢驗真理的唯一標準。

上一篇文章講了 GraphQL 的核心概念,所提到的一些例子都是理論化的,並沒有實際代碼做支撐,就好像在畫一個大餅,總是讓人不那麼信服。

它真的有那麼神奇嗎?那就同我一起看下去,用事實說話。

之前那篇文章一直有提到 GraphQL 是一個概念,每個語言可以有自己實現它的方式。因為,我是搞前端的,對 JavaScript 比較熟悉,所以,這裡就用 graphql-js(GraphQL 的 JavaScript 實現)來舉例。

Hello World

遵循傳統,第一個例子必須是 Hello World。

首先,安裝就不用多說了。

npm install graphql --save

那這個例子該怎麼設計哪?假設,查詢一個 hello 字元串,就返回一個 world 字元串,很明顯 type 的結構就該是這樣

type HelloWorld { hello: String}

如何實現這個 HelloWorld 類型哪?graphql-js 已經定義好了基礎類,我們直接調用就行。那麼,這個 type 實現起來也就非常簡單了

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; }, } })});

這樣類型就定義好了,還記不記得上篇文章提到的類型定義完成後該怎麼辦?

對,創建查詢的 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 里查詢 localhost:3000/graphql?{hello} 看看吧。

Blog System

在上一篇文章里,我們設計了一個博客的查詢 schema,這次我們就來動手實現它。(下面就開始講例子啦,不願聽我嘮叨的可以直接看代碼)

前面 HelloWorld 的例子講的比較詳細,現在大家熟悉了語法,接下來的案例就會過得快一些。

首先是 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)) } })});

兩個主要的類型已經定義好了,把它們倆整合起來就是博客類型了。

/** * 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 欄位,用來獲取查詢參數,如果在沒有設置過 arg 欄位的屬性上添加變數進行查詢,graphql-js 的驗證系統會報錯。

最後,將之前的 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 很好用,誰用誰知道。


推薦閱讀:

前端日刊-2018.01.16
HTTP入門(三):使用Nodo.js腳本實現簡易伺服器
Learning by Doing
七層網路協議--tcp/ip協議
移動web前端學習

TAG:前端開發 |