GraphQL 到底怎麼用?看看這個例子就知道了
GraphQL 究竟是什麼東西?
它實際上是一種 API 查詢語言。
GraphQL 顯示了伺服器可以提供的不同型別的資料,然後客戶端就可以明確選擇它們想要哪些內容。
在使用 GraphQL 時,你可以在一個呼叫中獲取多個伺服器的資源,而不是像 REST API 那樣需要呼叫多個 API。
理論說得再多也沒用,例子才是最直觀的。所以,讓我們開始使用 GraphQL 吧。
我們將在本文中使用 GraphQL 和 NodeJS。
先決條件
下載和安裝 NodeJS: https://nodejs.org/en/
如何在 NodeJS 中使用 GraphQL
GraphQL 可以與多種語言一起使用。在這裡,我們將重點介紹如何在 NodeJS 中使用 GraphQL。
建立一個叫作 graphql-with-nodejs 的資料夾。進入這個資料夾,並執行 npm init 來建立 NodeJS 專案。
複製程式碼
cd graphql-with-nodejs npminit
安裝依賴項
使用以下命令安裝 Express。
複製程式碼
npminstallexpress
使用以下命令安裝 GraphQL。我們將安裝 graphql 和 express-graphql。
複製程式碼
npminstallexpress-graphql graphql
NodeJS 程式碼
在專案中建立一個叫作 server.js 的檔案,並將下面的程式碼複製到檔案中。
複製程式碼
constexpress =require('express'); constport =5000; constapp = express(); app.get('/hello',(req,res) =>{ res.send("hello"); } ); app.listen(port); console.log(`Server Running at localhost:${port}`);
上面的程式碼提供了一個叫作 /hello 的 HTTP 端點。
這個端點是使用 express 建立的。
現在讓我們修改程式碼,啟用 GraphQL。
修改程式碼,啟用 GraphQL
GraphQL 將提供一個叫作 /graphql 的端點,負責處理所有的請求。
將下面的程式碼複製到 server.js 檔案中。
複製程式碼
//get all the libraries needed constexpress =require('express'); constgraphqlHTTP =require('express-graphql'); const{GraphQLSchema} =require('graphql'); const{queryType} =require('./query.js'); //setting up the port number and express app constport =5000; constapp = express(); // Define the Schema constschema =newGraphQLSchema({query: queryType }); //Setup the nodejs GraphQL server app.use('/graphql', graphqlHTTP({ schema: schema, graphiql:true, })); app.listen(port); console.log(`GraphQL Server Running at localhost:${port}`);
我們在 /graphql 端點上建立了一個 GraphQL 伺服器,它知道如何處理收到的請求。
GraphQL 伺服器是通過下面的程式碼建立起來的。
複製程式碼
app.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true, }));
現在讓我們來看一下 graphqlHTTP 的引數。
graphiql
graphiql 是一個 Web UI,你可以用它來測試 graphql 端點。我們將其設定為 true,這樣就可以很容易測試我們建立的各種 graphql 端點。
schema
雖然 graphql 只提供了一個外部端點 /graphql,但它可以擁有多個其他端點,用於執行其他各種操作。這些端點可以在 schema 中指定。
schema 將執行以下操作:
-
指定端點;
-
指定端點的輸入和輸出欄位;
-
指定在端點被呼叫時應該執行哪些操作,等等。
schema 的定義如下。
複製程式碼
const schema =newGraphQLSchema({query:queryType});
schema 可以包含查詢和可變型別,不過本文只關注查詢型別。
query
在這個定義中可以看到,query 已被設定為 queryType。
我們使用以下命令從 query.js 檔案匯入 queryType。
複製程式碼
const{queryType} =require('./query.js');
query.js 是一個自定義檔案,我們稍後會建立它。
在專案中建立一個叫作 query.js 的檔案,並將下面的程式碼複製到檔案中。
複製程式碼
const{ GraphQLObjectType, GraphQLString } =require('graphql'); //Define the Query constqueryType =newGraphQLObjectType({ name:'Query', fields: { hello: { type: GraphQLString, resolve:function(){ return"Hello World"; } } } }); exports.queryType = queryType;
有關這個 query 的說明
queryType 是一個 GraphQLObjectType 物件,並指定了名稱 Query。
我們在 fields 中指定各種端點,我們在這裡新增一個叫作 hello 的端點。
hello 的 type 是 GraphQLString,這意味著這個端點的返回型別為字串。因為這是 graphql schema,所以字串型別是 GraphQLString 而不是 String。如果直接使用 String 是不行的。
resolve 函式在呼叫端點時會被執行。這裡的操作是返回字串“Hello World”。
最後,我們使用 exports.queryType = queryType 匯出 queryType。這樣我們就可以在 server.js 中匯入它。
執行應用程式
使用以下命令執行這個應用程式。
複製程式碼
nodeserver.js
應用程式將執行在 localhost:5000/graphql 上。
你可以通過訪問 localhost:5000/graphql 來測試應用程式。
Graphiql Web UI 如下圖所示。
左側是輸入,右側是輸出。
給定以下輸入:
複製程式碼
{ hello }
將給出以下輸出:
複製程式碼
{ "data": { "hello":"Hello World" } }
新增更多端點
我們將建立 2 個新端點:
-
movie:根據給定的電影 ID 返回一部電影的資訊。
-
director:根據給定的導演 ID 返回導演的資訊,它還將返回該導演指導的所有電影資訊。
新增資料
通常,應用程式將從資料庫中讀取資料。但在本文中,我們只是簡單地在程式碼中硬編碼一些資料。
建立一個叫作 data.js 的檔案並新增以下程式碼。
複製程式碼
//Hardcode some data for movies and directors let movies = [{ id:1, name:"Movie 1", year:2018, directorId:1 }, { id:2, name:"Movie 2", year:2017, directorId:1 }, { id:3, name:"Movie 3", year:2016, directorId:3 } ]; let directors = [{ id:1, name:"Director 1", age:20 }, { id:2, name:"Director 2", age:30 }, { id:3, name:"Director 3", age:40 } ]; exports.movies = movies; exports.directors = directors;
這個檔案包含電影和導演的資料。我們將使用這個檔案中的資料作為端點的資料來源。
將 movie 端點新增到 query 中
新端點將被新增到 query.js 檔案的 queryType 中。
複製程式碼
movie: { type: movieType, args: { id:{ type: GraphQLInt } }, resolve:function(source, args){ return_.find(movies, {id:args.id}); } }
這個端點的返回型別是 movieType,我們會在後面定義它。
args 引數用於指定 movie 端點的輸入。這個端點的輸入是 id,型別是 GraphQLInt。
resolve 函式將從電影列表中返回與 id 對應的電影。find 是一個來自 lodash 庫的函式,用於查詢列表中的元素。
query.js 的完整程式碼如下所示。
複製程式碼
const{ GraphQLObjectType, GraphQLString, GraphQLInt } =require('graphql'); const_ =require('lodash'); const{movieType} =require('./types.js'); let{movies} =require('./data.js'); //Define the Query constqueryType =newGraphQLObjectType({ name:'Query', fields: { hello: { type: GraphQLString, resolve:function(){ return"Hello World"; } }, movie: { type: movieType, args: { id:{ type: GraphQLInt } }, resolve:function(source, args){ return_.find(movies, {id:args.id}); } } } }); exports.queryType = queryType;
從上面的程式碼可以看出,movieType 實際上是在 types.js 中定義的。
新增自定義型別 movieType
建立一個叫作 types.js 的檔案,將下面的程式碼新增到 types.js 檔案中。
複製程式碼
const { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLInt } = require('graphql'); // Define Movie Type movieType =newGraphQLObjectType({ name:'Movie', fields: { id: {type:GraphQLID}, name: {type:GraphQLString}, year: {type:GraphQLInt}, directorId: {type:GraphQLID} } }); exports.movieType = movieType;
可以看出,movieType 是一個 GraphQLObjectType 物件。
它有 4 個欄位 id、name、year 和 directorId。在新增這些欄位時同時也指定每個欄位的型別。
這些欄位直接來自之前定義的資料,也就是電影列表。
為 director 端點新增查詢和型別
與 movie 端點類似,我們也可以新增 director 端點。
複製程式碼
director: { type: directorType, args: { id:{ type: GraphQLInt } }, resolve:function(source, args){ return_.find(directors, {id:args.id}); } }
在 types.js 中新增 directorType。
複製程式碼
//Define Director Type directorType =newGraphQLObjectType({ name:'Director', fields: { id: {type:GraphQLID}, name: {type:GraphQLString}, age: {type:GraphQLInt}, movies: { type:newGraphQLList(movieType), resolve(source, args) { return_.filter(movies, { directorId: source.id }); } } } });
directorType 與 movieType 略有不同,為什麼會這樣?
為什麼 directorType 中會有一個 resolve 函式?之前我們只在 query 中看到過這個函式。
directorType 的不同之處
當 director 端點被呼叫時,我們必須返回導演以及導演所指導的所有電影的資訊。
directorType 中的前 3 個欄位 id、name、age 直接來自之前定義的資料(導演列表)。
第四個欄位 movies 需要包含這位導演所指導的電影列表。
為此,movies 欄位的型別是 GraphQLList。
但究竟如何才能找到這位導演指導的所有電影?
為此,我們在 movies 欄位中指定了 resolve 函式。這個函式的輸入是 source 和 args。
source 將持有父物件的詳細資訊。
假設某個導演的欄位 id = 1、name = “Random”、age = 20, 那麼 source.id = 1
source.name = “Random”、source.age = 20。
因此,在這個示例中,resolve 函式將找出 directorId 與給定導演 ID 相匹配的所有影片。
程式碼
這個應用程式的完整程式碼可以在 GitHub( https://github.com/aditya-sridhar/graphql-with-nodejs )上找到。
測試應用程式
現在讓我們根據不同的場景來測試這個應用程式。
使用 node server.js 執行應用程式.
訪問 localhost:5000/graphql,嘗試以下輸入。
movie
輸入:
複製程式碼
{ movie(id:1) { name } }
輸出:
複製程式碼
{ "data": { "movie": { "name":"Movie 1" } } }
從上面可以看出,客戶端可以明確地請求它想要的東西,GraphQL 確保只返回需要的引數。這裡只請求 name 欄位,所以伺服器只返回這個欄位的內容。
在 movie(id: 1) 中,id 是輸入引數。我們要求伺服器發回 id 為 1 的電影。
輸入:
複製程式碼
{ movie(id:3) { name id year } }
輸出:
複製程式碼
{ "data": { "movie": { "name":"Movie 3", "id":"3", "year":2016 } } }
在上面的示例中,請求了 name、id 和 year 欄位,所以伺服器返回所有這些欄位。
director
輸入:
複製程式碼
{ director(id:1) { name id, age } }
輸出:
複製程式碼
{ "data": { "director": { "name":"Director 1", "id":"1", "age":20 } } }
輸入:
複製程式碼
{ director(id:1) { name id, age, movies{ name, year } } }
輸出:
複製程式碼
{ "data": { "director": { "name":"Director 1", "id":"1", "age":20, "movies": [ { "name":"Movie 1", "year":2018 }, { "name":"Movie 2", "year":2017 } ] } } }
英文原文: https://dev.to/adityasridhar/what-is-graphql-and-how-to-use-it-1f58