搜索
首页web前端js教程理解 GraphQL:GraphQL 简介

理解 GraphQL:GraphQL 简介

概述

GraphQL 是一个令人兴奋的全新 API,用于即席查询和操作。它非常灵活并提供许多好处。它特别适合公开以图形和树形式组织的数据。 Facebook 于 2012 年开发了 GraphQL,并于 2015 年开源。

它迅速发展并成为最热门的技术之一。许多创新公司在生产中采用并使用了 GraphQL。在本教程中,您将学习:

  • GraphQL 的原理
  • 与 REST 相比如何
  • 如何设计架构
  • 如何设置 GraphQL 服务器
  • 如何实现查询和变更
  • 以及一些其他高级主题

GraphQL 的亮点在哪里?

当您的数据按层次结构或图形组织并且前端希望访问该层次结构或图形的不同子集时,GraphQL 处于最佳状态。考虑一个公开 NBA 的应用程序。你有球队、球员、教练、冠军,以及每一项的大量信息。以下是一些示例查询:

  • 金州勇士队目前名单上的球员姓名是什么?
  • 华盛顿奇才队先发球员的姓名、身高和年龄是多少?
  • 哪位现役教练获得的冠军最多?
  • 教练在哪几年为哪些球队赢得了冠军?
  • 哪位球员获得 MVP 奖项最多?

我可以提出数百个这样的查询。想象一下,您必须设计一个 API 将所有这些查询公开给前端,并且当您的用户或产品经理提出新的令人兴奋的查询内容时,能够使用新的查询类型轻松扩展 API。

这并不是一件小事。 GraphQL 旨在解决这个确切的问题,并且通过单个 API 端点,它提供了巨大的功能,您很快就会看到。

GraphQL 与 REST

在深入了解 GraphQL 的具体细节之前,我们先将其与 REST 进行比较,后者是目前最流行的 Web API 类型。

REST 遵循面向资源的模型。如果我们的资源是球员、教练和球队,那么可能会有这样的端点:

  • /玩家
  • /玩家/ 
  • /教练
  • /教练/ 
  • /团队
  • /团队/

通常,没有 id 的端点仅返回 id 列表,而具有 id 的端点则返回一个资源的完整信息。当然,您可以以其他方式设计 API(例如 /players 端点可能还返回每个玩家的姓名或有关每个玩家的所有信息)。

在动态环境中,这种方法的问题在于,您要么获取不足(例如,您只获取 id 并且需要更多信息),要么过度获取(例如,在你只是对名字感兴趣)。

这些都是难题。获取不足时,如果您获取 100 个 id,则需要执行 100 个单独的 API 调用才能获取每个玩家的信息。当过度获取时,您会浪费大量后端时间和网络带宽来准备和传输大量不需要的数据。

有多种方法可以通过 REST 来解决这个问题。您可以设计许多定制端点,每个端点都准确返回您需要的数据。该解决方案不可扩展。保持 API 的一致性是很困难的。很难进化它。很难记录和使用它。当这些定制端点之间存在大量重叠时,很难维护它。

考虑这些额外的端点:

  • /玩家/姓名
  • /players/names_and_championships
  • /团队/首发

另一种方法是保留少量通用端点,但提供大量查询参数。该解决方案避免了多端点问题,但它违背了 REST 模型的原则,而且难以一致地发展和维护。

你可以说 GraphQL 已经将这种方法发挥到了极限。它并不考虑明确定义的资源,而是考虑整个域的子图。

GraphQL 类型系统

GraphQL 使用由类型和属性组成的类型系统对域进行建模。每个属性都有一个类型。属性类型可以是 GraphQL 提供的基本类型之一,例如 ID、String 和 Boolean,也可以是用户定义的类型。图的节点是用户定义的类型,边是具有用户定义类型的属性。

例如,如果“玩家”类型具有“团队”类型的“团队”属性,则意味着每个玩家节点到团队节点之间存在一条边。所有类型都在描述 GraphQL 域对象模型的架构中定义。

这是 NBA 域的一个非常简化的架构。球员有一个名字,与他最相关的球队(是的,我知道球员有时会从一支球队转到另一支球队),以及球员赢得的冠军数量。

球队有名字、球员阵容以及球队赢得的冠军数量。

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

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

还有预定义的入口点。它们是查询、变更和订阅。前端通过入口点与后端进行通信,并根据需要进行自定义。

这是一个简单返回所有玩家的查询:

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

以上是理解 GraphQL:GraphQL 简介的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript引擎:比较实施JavaScript引擎:比较实施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

超越浏览器:现实世界中的JavaScript超越浏览器:现实世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

使用Next.js(后端集成)构建多租户SaaS应用程序使用Next.js(后端集成)构建多租户SaaS应用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务

如何使用Next.js(前端集成)构建多租户SaaS应用程序如何使用Next.js(前端集成)构建多租户SaaS应用程序Apr 11, 2025 am 08:22 AM

本文展示了与许可证确保的后端的前端集成,并使用Next.js构建功能性Edtech SaaS应用程序。 前端获取用户权限以控制UI的可见性并确保API要求遵守角色库

JavaScript:探索网络语言的多功能性JavaScript:探索网络语言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

JavaScript的演变:当前的趋势和未来前景JavaScript的演变:当前的趋势和未来前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

神秘的JavaScript:它的作用以及为什么重要神秘的JavaScript:它的作用以及为什么重要Apr 09, 2025 am 12:07 AM

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

Python还是JavaScript更好?Python还是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。 1.Python以简洁语法和丰富库生态着称,适用于数据分析和Web开发。 2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)