Maison  >  Article  >  interface Web  >  Comprendre GraphQL : introduction à GraphQL

Comprendre GraphQL : introduction à GraphQL

王林
王林original
2023-08-28 22:05:121381parcourir

理解 GraphQL:GraphQL 简介

Vue d'ensemble

GraphQL est une nouvelle API passionnante pour les requêtes et les opérations ad hoc. Il est très flexible et offre de nombreux avantages. Il est particulièrement adapté à l’exposition de données organisées sous forme de graphiques et d’arborescences. Facebook a développé GraphQL en 2012 et l'a rendu open source en 2015.

Il se développe rapidement et devient l’une des technologies les plus en vogue. De nombreuses entreprises innovantes ont adopté et utilisé GraphQL en production. Dans ce tutoriel, vous apprendrez :

  • Le principe de GraphQL
  • Comment cela se compare-t-il à REST
  • Comment concevoir une architecture
  • Comment configurer un serveur GraphQL
  • Comment implémenter la requête et le changement
  • Et quelques autres thèmes premium

Quels sont les points forts de GraphQL ?

GraphQL est à son meilleur lorsque vos données sont organisées dans une hiérarchie ou un graphique et que le front-end souhaite accéder à différents sous-ensembles de cette hiérarchie ou de ce graphique. Considérons une application qui expose la NBA. Vous avez des équipes, des joueurs, des entraîneurs, des championnats et des tonnes d'informations sur chacun. Voici quelques exemples de requêtes :

  • Quels sont les noms des joueurs actuellement présents sur la liste des Golden State Warriors ?
  • Quels sont les noms, tailles et âges des joueurs débutants des Washington Wizards ?
  • Quel entraîneur actuel a remporté le plus de championnats ?
  • Au cours de quelles années l'entraîneur a-t-il remporté des championnats pour quelles équipes ?
  • Quel joueur a remporté le plus de prix MVP ?

Je pourrais poser des centaines de requêtes comme celle-ci. Imaginez devoir concevoir une API qui expose toutes ces requêtes au front-end et être capable d'étendre facilement l'API avec de nouveaux types de requêtes lorsque vos utilisateurs ou chefs de produit proposent un contenu de requête nouveau et passionnant.

Ce n'est pas une petite chose. GraphQL a été conçu pour résoudre exactement ce problème, et avec un seul point de terminaison d'API, il offre d'énormes fonctionnalités, comme vous le verrez bientôt.

GraphQL et REST

Avant d'entrer dans les détails de GraphQL, comparons-le à REST, qui est actuellement le type d'API Web le plus populaire.

REST suit un modèle orienté ressources. Si nos ressources étaient des joueurs, des entraîneurs et des équipes, nous pourrions avoir des résultats comme celui-ci :

  • /joueur
  • /joueur/
  • /entraîneur
  • /entraîneur/
  • /équipe
  • /équipe/

Habituellement, les points de terminaison sans identifiant renvoient uniquement une liste d'identifiants, tandis que les points de terminaison avec identifiant renvoient des informations complètes sur une ressource. Bien sûr, vous pouvez concevoir l'API d'autres manières (par exemple, le point de terminaison /players peut également renvoyer le nom de chaque joueur ou toutes les informations sur chaque joueur).

Dans un environnement dynamique, le problème avec cette approche est que vous effectuez soit une extraction insuffisante (par exemple, vous obtenez uniquement l'identifiant et avez besoin de plus d'informations), soit une extraction excessive (par exemple, lorsque vous n'êtes intéressé que par le nom).

Ce sont des questions difficiles. Lorsque vous n'en avez pas assez, si vous obtenez 100 identifiants, vous devez effectuer 100 appels API distincts pour obtenir les informations de chaque joueur. Lorsque vous effectuez une extraction excessive, vous perdez beaucoup de temps back-end et de bande passante réseau à préparer et à transmettre de grandes quantités de données dont vous n'avez pas besoin.

Il existe plusieurs façons de résoudre ce problème via REST. Vous pouvez concevoir de nombreux points de terminaison personnalisés, chacun renvoyant exactement les données dont vous avez besoin. Cette solution n'est pas évolutive. Maintenir la cohérence des API est difficile. Il est difficile de le faire évoluer. Il est difficile de l'enregistrer et de l'utiliser. Il est difficile de le maintenir lorsqu'il existe de nombreux chevauchements entre ces points de terminaison personnalisés.

Considérez ces points de terminaison supplémentaires :

  • /joueur/nom
  • /joueurs/noms_et_championships
  • /équipe/départ

Une autre approche consiste à conserver un petit nombre de points de terminaison communs mais à fournir un grand nombre de paramètres de requête. Cette solution évite le problème des points de terminaison multiples, mais elle viole les principes du modèle REST et est difficile à développer et à maintenir de manière cohérente.

On pourrait dire que GraphQL a poussé cette approche jusqu'à ses limites. Il ne considère pas les ressources explicitement définies, mais plutôt un sous-graphe du domaine entier.

Système de type GraphQL

GraphQL modélise les domaines à l'aide d'un système de types composé de types et de propriétés. Chaque propriété a un type. Le type de propriété peut être l'un des types de base fournis par GraphQL, tels que ID, String et Boolean, ou il peut s'agir d'un type défini par l'utilisateur. Les nœuds d'un graphique sont des types définis par l'utilisateur et les arêtes sont des attributs avec des types définis par l'utilisateur.

Par exemple, si le type "Joueur" a un attribut "Équipe" de type "Équipe", cela signifie qu'il y a un bord de chaque nœud joueur au nœud équipe. Tous les types sont définis dans un schéma qui décrit le modèle objet de domaine GraphQL.

Il s'agit d'une architecture très simplifiée pour le domaine NBA. Le joueur a un nom, l'équipe à laquelle il est le plus associé (oui, je sais que les joueurs passent parfois d'une équipe à l'autre) et le nombre de championnats qu'il a remportés.

Les équipes ont des noms, des effectifs et le nombre de championnats qu'elles ont remportés.

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

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

Il existe également des points d'entrée prédéfinis. Ils interrogent, modifient et s'abonnent. Le frontend communique avec le backend via des points d'entrée et peut être personnalisé selon les besoins.

Voici une requête qui renvoie simplement tous les joueurs :

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了。我预测它将很快成为前端的主要内容。试一试。你会喜欢的。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn