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
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章