Heim  >  Artikel  >  Web-Frontend  >  GraphQL verstehen: Einführung in GraphQL

GraphQL verstehen: Einführung in GraphQL

王林
王林Original
2023-08-28 22:05:121381Durchsuche

理解 GraphQL:GraphQL 简介

Übersicht

GraphQL ist eine aufregende neue API für Ad-hoc-Abfragen und -Vorgänge. Es ist sehr flexibel und bietet viele Vorteile. Es eignet sich besonders für die Darstellung von Daten, die in Diagramm- und Baumform organisiert sind. Facebook entwickelte GraphQL im Jahr 2012 und stellte es 2015 als Open Source zur Verfügung.

Es entwickelt sich rasant und wird zu einer der heißesten Technologien. Viele innovative Unternehmen haben GraphQL übernommen und in der Produktion eingesetzt. In diesem Tutorial lernen Sie:

  • Das Prinzip von GraphQL
  • Wie ist es im Vergleich zu REST
  • Wie man Architektur entwirft
  • So richten Sie einen GraphQL-Server ein
  • So implementieren Sie Abfragen und Änderungen
  • Und einige andere Premium-Themen

Was sind die Highlights von GraphQL?

GraphQL ist am besten geeignet, wenn Ihre Daten in einer Hierarchie oder einem Diagramm organisiert sind und das Frontend auf verschiedene Teilmengen dieser Hierarchie oder dieses Diagramms zugreifen möchte. Stellen Sie sich eine Anwendung vor, die die NBA offenlegt. Sie haben Teams, Spieler, Trainer, Meisterschaften und jede Menge Informationen zu jedem. Hier sind einige Beispielabfragen:

  • Wie heißen die Spieler, die derzeit im Kader der Golden State Warriors stehen?
  • Wie lauten die Namen, die Größe und das Alter der Startspieler der Washington Wizards?
  • Welcher aktueller Trainer hat die meisten Meisterschaften gewonnen?
  • In welchen Jahren hat der Trainer für welche Mannschaften Meisterschaften gewonnen?
  • Welcher Spieler hat die meisten MVP-Auszeichnungen gewonnen?

Ich könnte Hunderte solcher Fragen stellen. Stellen Sie sich vor, Sie müssten eine API entwerfen, die alle diese Abfragen dem Front-End zur Verfügung stellt, und die API problemlos um neue Abfragetypen erweitern, wenn Ihre Benutzer oder Produktmanager neue und aufregende Abfrageinhalte entwickeln.

Das ist keine Kleinigkeit. GraphQL wurde entwickelt, um genau dieses Problem zu lösen, und mit einem einzigen API-Endpunkt bietet es enorme Funktionalität, wie Sie gleich sehen werden.

GraphQL und REST

Bevor wir auf die Besonderheiten von GraphQL eingehen, vergleichen wir es mit REST, dem derzeit beliebtesten Web-API-Typ.

REST folgt einem ressourcenorientierten Modell. Wenn unsere Ressourcen Spieler, Trainer und Teams wären, könnten wir Endpunkte wie diesen haben:

  • /Spieler
  • /Spieler/
  • /Trainer
  • /Trainer/
  • /Team
  • /team/

Normalerweise geben Endpunkte ohne IDs nur eine Liste von IDs zurück, während Endpunkte mit IDs vollständige Informationen über eine Ressource zurückgeben. Natürlich könnten Sie die API auch auf andere Weise gestalten (z. B. könnte der /players-Endpunkt auch den Namen jedes Spielers oder alle Informationen zu jedem Spieler zurückgeben).

Das Problem bei diesem Ansatz in einer dynamischen Umgebung besteht darin, dass Sie entweder zu wenig abrufen (z. B. Sie erhalten nur die ID und benötigen weitere Informationen) oder zu viel abrufen (z. B. sind Sie nur am Namen interessiert).

Das sind schwierige Fragen. Wenn Sie nicht genug bekommen und 100 IDs erhalten, müssen Sie 100 separate API-Aufrufe durchführen, um die Informationen jedes Spielers zu erhalten. Wenn Sie zu viel abrufen, verschwenden Sie viel Backend-Zeit und Netzwerkbandbreite mit der Vorbereitung und Übertragung großer Datenmengen, die Sie nicht benötigen.

Es gibt mehrere Möglichkeiten, dieses Problem über REST zu lösen. Sie können viele benutzerdefinierte Endpunkte entwerfen, von denen jeder genau die Daten zurückgibt, die Sie benötigen. Diese Lösung ist nicht skalierbar. Es ist schwierig, APIs konsistent zu halten. Es ist schwer, es weiterzuentwickeln. Es ist schwierig, es aufzuzeichnen und zu verwenden. Es ist schwierig, es aufrechtzuerhalten, wenn es viele Überschneidungen zwischen diesen benutzerdefinierten Endpunkten gibt.

Berücksichtigen Sie diese zusätzlichen Endpunkte:

  • /Spieler/Name
  • /Spieler/Namen_und_Meisterschaften
  • /team/starting

Ein anderer Ansatz besteht darin, eine kleine Anzahl gemeinsamer Endpunkte beizubehalten, aber eine große Anzahl von Abfrageparametern bereitzustellen. Diese Lösung vermeidet das Problem mehrerer Endpunkte, verstößt jedoch gegen die Prinzipien des REST-Modells und ist schwierig konsistent zu entwickeln und zu warten.

Man könnte sagen, GraphQL hat diesen Ansatz an seine Grenzen gebracht. Es berücksichtigt keine explizit definierten Ressourcen, sondern einen Teilgraphen der gesamten Domäne.

GraphQL-Typsystem

GraphQL modelliert Domänen mithilfe eines Typsystems, das aus Typen und Eigenschaften besteht. Jede Eigenschaft hat einen Typ. Der Eigenschaftstyp kann einer der von GraphQL bereitgestellten Grundtypen sein, z. B. ID, String und Boolean, oder es kann ein benutzerdefinierter Typ sein. Die Knoten eines Diagramms sind benutzerdefinierte Typen und die Kanten sind Attribute mit benutzerdefinierten Typen.

Wenn der Typ „Spieler“ beispielsweise ein „Team“-Attribut vom Typ „Team“ hat, bedeutet dies, dass es eine Kante von jedem Spielerknoten zum Teamknoten gibt. Alle Typen werden in einem Schema definiert, das das GraphQL-Domänenobjektmodell beschreibt.

Dies ist eine sehr vereinfachte Architektur für die NBA-Domäne. Der Spieler hat einen Namen, die Mannschaft, mit der er am meisten in Verbindung gebracht wird (ja, ich weiß, dass Spieler manchmal von einer Mannschaft zur anderen wechseln) und die Anzahl der Meisterschaften, die der Spieler gewonnen hat.

Teams haben Namen, Kader und die Anzahl der Meisterschaften, die das Team gewonnen hat.

type Player {
    id: ID
	name: String!
	team: Team!
	championshipCount: Integer!
}

type Team {
	id: ID
	name: String!
	players: [Player!]!
	championshipCount: Integer!
}

Es gibt auch vordefinierte Einstiegspunkte. Sie sind Abfragen, Ändern und Abonnieren. Das Frontend kommuniziert über Einstiegspunkte mit dem Backend und kann bei Bedarf angepasst werden.

Hier ist eine Abfrage, die einfach alle Spieler zurückgibt:

type Query {
    allPlayers: [Player!]!
}

感叹号表示该值不能为空。对于 allPlayers 查询,它可以返回空列表,但不能返回 null。另外,这意味着列表中不能有空玩家(因为它包含 Player!)。

设置 GraphQL 服务器

这是一个基于 Node-Express 的成熟 GraphQL 服务器。它有一个内存中的硬编码数据存储。通常,数据将位于数据库中或从其他服务获取。数据定义如下(如果您最喜欢的球队或球员未能入选,请提前致歉):

let data = {
  "allPlayers": {
    "1": {
      "id": "1",
      "name": "Stephen Curry",
      "championshipCount": 2,
      "teamId": "3"
    },
    "2": {
      "id": "2",
      "name": "Michael Jordan",
      "championshipCount": 6,
      "teamId": "1"
    },
    "3": {
      "id": "3",
      "name": "Scottie Pippen",
      "championshipCount": 6,
      "teamId": "1"
    },
    "4": {
      "id": "4",
      "name": "Magic Johnson",
      "championshipCount": 5,
      "teamId": "2"
    },
    "5": {
      "id": "5",
      "name": "Kobe Bryant",
      "championshipCount": 5,
      "teamId": "2"
    },
    "6": {
      "id": "6",
      "name": "Kevin Durant",
      "championshipCount": 1,
      "teamId": "3"
    }
  },

  "allTeams": {
    "1": {
      "id": "1",
      "name": "Chicago Bulls",
      "championshipCount": 6,
      "players": []
    },
    "2": {
      "id": "2",
      "name": "Los Angeles Lakers",
      "championshipCount": 16,
      "players": []
    },
    "3": {
      "id": "3",
      "name": "Golden State Warriors",
      "championshipCount": 5,
      "players": []
    }
  }
}

我使用的库是:

const express = require('express');
const graphqlHTTP = require('express-graphql');
const app = express();
const { buildSchema } = require('graphql');
const _ = require('lodash/core');

这是构建架构的代码。请注意,我向 allPlayers 根查询添加了几个变量。

schema = buildSchema(`
  type Player {
    id: ID
    name: String!
    championshipCount: Int!
    team: Team!
  }
  
  type Team {
    id: ID
    name: String!
    championshipCount: Int!
    players: [Player!]!
  }
  
  type Query {
    allPlayers(offset: Int = 0, limit: Int = -1): [Player!]!
  }`

关键部分来了:连接查询并实际提供数据。 rootValue 对象可能包含多个根。

这里,只有 allPlayers。它从参数中提取偏移量和限制,对所有玩家数据进行切片,然后根据团队 ID 设置每个玩家的团队。这使得每个玩家都是一个嵌套对象。

rootValue = {
  allPlayers: (args) => {
    offset = args['offset']
    limit = args['limit']
    r = _.values(data["allPlayers"]).slice(offset)
    if (limit > -1) {
      r = r.slice(0, Math.min(limit, r.length))
    }
    _.forEach(r, (x) => {
      data.allPlayers[x.id].team   = data.allTeams[x.teamId]
    })
    return r
  },
}

最后,这是 graphql 端点,传递架构和根值对象:

app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true
}));

app.listen(3000);

module.exports = app;

graphiql 设置为 true 使我们能够使用出色的浏览器内 GraphQL IDE 来测试服务器。我强烈推荐它来尝试不同的查询。

使用 GraphQL 进行临时查询

万事俱备。让我们导航到 http://localhost:3000/graphql 并享受一些乐趣。

我们可以从简单的开始,只包含玩家姓名列表:

query justNames {
    allPlayers {
    name
  }
}

Output:

{
  "data": {
    "allPlayers": [
      {
        "name": "Stephen Curry"
      },
      {
        "name": "Michael Jordan"
      },
      {
        "name": "Scottie Pippen"
      },
      {
        "name": "Magic Johnson"
      },
      {
        "name": "Kobe Bryant"
      },
      {
        "name": "Kevin Durant"
      }
    ]
  }
}

好吧。我们这里有一些超级巨星。毫无疑问。让我们尝试一些更奇特的东西:从偏移量 4 开始,有 2 名玩家。对于每个球员,返回他们的名字和他们赢得的冠军数量以及他们的球队名称和球队赢得的冠军数量。

query twoPlayers {
    allPlayers(offset: 4, limit: 2) {
    name
    championshipCount
    team {
      name
      championshipCount
    }
  }
}

Output:

{
  "data": {
    "allPlayers": [
      {
        "name": "Kobe Bryant",
        "championshipCount": 5,
        "team": {
          "name": "Los Angeles Lakers",
          "championshipCount": 16
        }
      },
      {
        "name": "Kevin Durant",
        "championshipCount": 1,
        "team": {
          "name": "Golden State Warriors",
          "championshipCount": 5
        }
      }
    ]
  }
}

所以科比·布莱恩特随湖人队赢得了 5 个总冠军,湖人队总共获得了 16 个总冠军。凯文·杜兰特在勇士队只赢得了一次总冠军,而勇士队总共赢得了五次总冠军。

GraphQL 突变

魔术师约翰逊无疑是场上的魔术师。但如果没有他的朋友卡里姆·阿卜杜勒·贾巴尔,他不可能做到这一点。让我们将 Kareem 添加到我们的数据库中。我们可以定义 GraphQL 突变来执行从图表中添加、更新和删除数据等操作。

首先,让我们向架构添加突变类型。它看起来有点像函数签名:

type Mutation {
    createPlayer(name: String, 
                 championshipCount: Int, 
                 teamId: String): Player
}

然后,我们需要实现它并将其添加到根值中。该实现只是获取查询提供的参数并将一个新对象添加到 data['allPlayers']。它还确保正确设置团队。最后,它返回新的玩家。

  createPlayer: (args) => {
    id = (_.values(data['allPlayers']).length + 1).toString()
    args['id'] = id
    args['team'] = data['allTeams'][args['teamId']]
    data['allPlayers'][id] = args
    return data['allPlayers'][id]
  },

要实际添加 Kareem,我们可以调用突变并查询返回的玩家:

mutation addKareem {
  createPlayer(name: "Kareem Abdul-Jabbar", 
               championshipCount: 6, 
               teamId: "2") {
    name
    championshipCount
    team {
      name
    }
  }
}

Output:

{
  "data": {
    "createPlayer": {
      "name": "Kareem Abdul-Jabbar",
      "championshipCount": 6,
      "team": {
        "name": "Los Angeles Lakers"
      }
    }
  }
}

这是一个关于突变的黑暗小秘密......它们实际上与查询完全相同。您可以在查询中修改数据,并且可能只从突变中返回数据。 GraphQL 不会查看您的代码。查询和突变都可以接受参数并返回数据。它更像是语法糖,可以使您的架构更具人类可读性。

高级主题

订阅

订阅是 GraphQL 的另一个杀手级功能。通过订阅,客户端可以订阅每当服务器状态发生变化时就会触发的事件。订阅是后来引入的,不同的框架以不同的方式实现。

验证

GraphQL 将验证针对架构的每个查询或突变。当输入数据具有复杂形状时,这是一个巨大的胜利。您不必编写烦人且脆弱的验证代码。 GraphQL 将为您处理好它。

架构自省

您可以检查和查询当前架构本身。这为您提供了动态发现架构的元能力。下面是一个返回所有类型名称及其描述的查询:

query q {
  __schema {
    types {
      name
      description            
    }
  }

结论

GraphQL 是一项令人兴奋的新 API 技术,与 REST API 相比,它具有许多优势。它背后有一个充满活力的社区,更不用说Facebook了。我预测它将很快成为前端的主要内容。试一试。你会喜欢的。

Das obige ist der detaillierte Inhalt vonGraphQL verstehen: Einführung in GraphQL. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn