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
npm init
安裝依賴項
使用以下命令安裝Express。
npm install express
使用以下命令安裝GraphQL。我們將安裝graphql和express-graphql。
npm install express-graphql graphql
NodeJS代碼
在項目中創建一個叫作server.js的文件,並將下面的代碼複製到文件中。
const express = require('express');
const port = 5000;
const app = 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
const express = require('express');
const graphqlHTTP = require('express-graphql');
const {GraphQLSchema} = require('graphql');
const {queryType} = require('./query.js');
//setting up the port number and express app
const port = 5000;
const app = express();
// Define the Schema
const schema = new GraphQLSchema({ 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 = new GraphQLSchema({ 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
const queryType = new GraphQLObjectType({
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中導入它。
運行應用程序
使用以下命令運行這個應用程序。
node server.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
const queryType = new GraphQLObjectType({
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 = new GraphQLObjectType({
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 = new GraphQLObjectType({
name: 'Director',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
age: { type: GraphQLInt },
movies: {
type: new GraphQLList(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