第19课:从外部 API 获取数据
HTTP Client 扩展允许我们向 Web 服务器执行 HTTP 请求。
本教程课程演示如何从外部 API 获取数据,方式包括:
- 从 Mailchimp 的 REST API 获取邮件列表成员,提取其电子邮件地址,并对数据进行处理
- 从 GitHub 的 GraphQL API 获取代码仓库
执行 HTTP 请求
Mailchimp API 文档说明,要获取邮件列表成员数据,需要向 REST API 发送 GET 请求:
curl --request GET \
--url 'https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members' \
--user 'username:password'让我们在 Gato GraphQL 中实现同样的操作。
通过全局字段 _sendHTTPRequest(由 HTTP Client 扩展提供)执行 HTTP 请求:
query {
_sendHTTPRequest(input: {
url: "https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members",
method: GET,
options: {
auth: {
username: "{USER}",
password: "{API_TOKEN}"
}
}
}) {
body
contentType
statusCode
headers
serverHeader: header(name: "Server")
}
}字段 _sendHTTPRequest 返回一个 HTTPResponse 类型的对象。执行该 query 后,可以看到 body 字段(String 类型)包含响应的原始内容:
{
"data": {
"_sendHTTPRequest": {
"body": "{\"members\":[{\"id\":\"mSjGOg5qSb3dKTxPU9lhRZCxHGug8Mrt\",\"email_address\":\"vinesh@yahoo.com\",\"unique_email_id\":\"KObAXbEO3X\",\"contact_id\":\"JiCdz5EY67m3PKugW3bRE9VI1WjiBbjq\",\"full_name\":\"Vinesh Munak\",\"web_id\":443344389,\"email_type\":\"html\",\"status\":\"subscribed\",\"consents_to_one_to_one_messaging\":true,\"merge_fields\":{\"FNAME\":\"Vinesh\",\"LNAME\":\"Munak\",\"ADDRESS\":{\"addr1\":\"\",\"addr2\":\"\",\"city\":\"\",\"state\":\"\",\"zip\":\"\",\"country\":\"IN\"},\"PHONE\":\"\",\"BIRTHDAY\":\"\"},\"stats\":{\"avg_open_rate\":0.8,\"avg_click_rate\":0.6},\"ip_signup\":\"\",\"timestamp_signup\":\"\",\"ip_opt\":\"218.115.112.129\",\"timestamp_opt\":\"2020-12-31T06:55:17+00:00\",\"member_rating\":4,\"last_changed\":\"2020-12-31T06:55:17+00:00\",\"language\":\"\",\"vip\":false,\"email_client\":\"\",\"location\":{\"latitude\":2.18,\"longitude\":99.47,\"gmtoff\":8,\"dstoff\":8,\"country_code\":\"MY\",\"timezone\":\"asia/kuala_lumpur\",\"region\":\"10\"},\"source\":\"Admin Add\",\"tags_count\":0,\"tags\":[],\"list_id\":\"9nrwpfj0ou\",\"_links\":[{...}]},{...}],\"total_items\":4927,\"_links\":[{...}]}",
"contentType": "application/json; charset=utf-8",
"statusCode": 200,
"headers": {
"Server": "openresty",
"Content-Type": "application/json; charset=utf-8",
"Vary": "Accept-Encoding",
"X-Request-Id": "177551d0-82e9-3d61-a664-177f61b91f80",
"Link": "<https://us7.api.mailchimp.com/schema/3.0/Lists/Members/Collection.json>; rel=\"describedBy\"",
"Date": "Thu, 13 Jul 2023 04:57:42 GMT",
"Transfer-Encoding": "chunked",
"Connection": "keep-alive,Transfer-Encoding"
},
"serverHeader": "openresty"
}
}
}由于响应的 content-type 为 application/json,我们可以通过字段 _strDecodeJSONObject(来自 PHP Functions Via Schema 扩展)将原始 body 内容从 String 转换为 JSONObject:
query {
_sendHTTPRequest(input: {
url: "https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members",
method: GET,
options: {
auth: {
username: "{USER}",
password: "{API_TOKEN}"
}
}
}) {
body @remove
bodyJSONObject: _strDecodeJSONObject(string: $__body)
}
}现在 body 可以作为 JSON 对象访问:
{
"data": {
"_sendHTTPRequest": {
"bodyJSONObject": {
"members": [
{
"id": "mSjGOg5qSb3dKTxPU9lhRZCxHGug8Mrt",
"email_address": "vinesh@yahoo.com",
"unique_email_id": "KObAXbEO3X",
"contact_id": "JiCdz5EY67m3PKugW3bRE9VI1WjiBbjq",
"full_name": "Vinesh Munak",
"web_id": 443344389,
"email_type": "html",
"status": "subscribed",
"consents_to_one_to_one_messaging": true,
"merge_fields": {
"FNAME": "Vinesh",
"LNAME": "Munak",
"ADDRESS": {
"addr1": "",
"addr2": "",
"city": "",
"state": "",
"zip": "",
"country": "IN"
},
"PHONE": "",
"BIRTHDAY": ""
},
"stats": {
"avg_open_rate": 0.8,
"avg_click_rate": 0.6
},
"ip_signup": "",
"timestamp_signup": "",
"ip_opt": "218.115.112.129",
"timestamp_opt": "2020-12-31T06:55:17+00:00",
"member_rating": 4,
"last_changed": "2020-12-31T06:55:17+00:00",
"language": "",
"vip": false,
"email_client": "",
"location": {
"latitude": 2.18,
"longitude": 99.47,
"gmtoff": 8,
"dstoff": 8,
"country_code": "MY",
"timezone": "asia/kuala_lumpur",
"region": "10"
},
"source": "Admin Add",
"tags_count": 0,
"tags": [],
"list_id": "9nrwpfj0ou",
"_links": [
{
// ...
},
// ...
]
},
{
// ...
}
],
"list_id": "9nrwpfj0ou",
"total_items": 4927,
"_links": [
{
// ...
},
// ...
]
}
}
}
}连接到 REST API
HTTP Client 还提供了已经处理 content-type 为 application/json 响应的函数字段,适合用于连接 REST API:
_sendJSONObjectItemHTTPRequest:当内容对应单个 JSON 对象时_sendJSONObjectCollectionHTTPRequest:当内容对应 JSON 对象集合时
这些字段已将响应转换为 JSONObject 或 [JSONObject]。
这些字段要求响应的状态码为成功状态(即 200-299 范围内,如 200、201 或 202),这样它们就能直接返回包含 JSON 解码后响应 body 的 JSONObject。
若状态码不在此范围内,GraphQL 响应将包含相应的错误。
例如,当从 WP REST API 端点 /wp-json/wp/v2/posts/{postId}/ 获取不存在的文章时,响应将为:
{
"errors": [
{
"message": "Client error: `GET https://newapi.getpop.org/wp-json/wp/v2/posts/88888/` resulted in a `404 Not Found` response:\n{\"code\":\"rest_post_invalid_id\",\"message\":\"Invalid post ID.\",\"data\":{\"status\":404}}\n",
"locations": [
{
"line": 3,
"column": 17
}
],
"extensions": {
"path": [
"externalData: _sendJSONObjectItemHTTPRequest(input: {url: \"https://newapi.getpop.org/wp-json/wp/v2/posts/88888/\"}) @export(as: \"externalData\")",
"query ConnectToAPI { ... }"
],
"type": "QueryRoot",
"field": "externalData: _sendJSONObjectItemHTTPRequest(input: {url: \"https://newapi.getpop.org/wp-json/wp/v2/posts/88888/\"}) @export(as: \"externalData\")",
"id": "root",
"code": "PoP/ComponentModel@e1"
}
}
],
"data": {
"externalData": null
}
}如果不想将非 200s 状态码(如 302、404 或 500)视为错误,必须使用 _sendHTTPRequest 字段。
改写前面的 query:
query {
_sendJSONObjectItemHTTPRequest(input: {
url: "https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members",
method: GET,
options: {
auth: {
username: "{USER}",
password: "{API_TOKEN}"
}
}
})
}...将产生如下响应:
{
"data": {
"_sendJSONObjectItemHTTPRequest": {
"members": [
{
"id": "mSjGOg5qSb3dKTxPU9lhRZCxHGug8Mrt",
"email_address": "vinesh@yahoo.com",
"unique_email_id": "KObAXbEO3X",
"contact_id": "JiCdz5EY67m3PKugW3bRE9VI1WjiBbjq",
"full_name": "Vinesh Munak",
"web_id": 443344389,
"email_type": "html",
"status": "subscribed",
"consents_to_one_to_one_messaging": true,
"merge_fields": {
"FNAME": "Vinesh",
"LNAME": "Munak",
"ADDRESS": {
"addr1": "",
"addr2": "",
"city": "",
"state": "",
"zip": "",
"country": "IN"
},
"PHONE": "",
"BIRTHDAY": ""
},
"stats": {
"avg_open_rate": 0.8,
"avg_click_rate": 0.6
},
"ip_signup": "",
"timestamp_signup": "",
"ip_opt": "218.115.112.129",
"timestamp_opt": "2020-12-31T06:55:17+00:00",
"member_rating": 4,
"last_changed": "2020-12-31T06:55:17+00:00",
"language": "",
"vip": false,
"email_client": "",
"location": {
"latitude": 2.18,
"longitude": 99.47,
"gmtoff": 8,
"dstoff": 8,
"country_code": "MY",
"timezone": "asia/kuala_lumpur",
"region": "10"
},
"source": "Admin Add",
"tags_count": 0,
"tags": [],
"list_id": "9nrwpfj0ou",
"_links": [
{
// ...
},
// ...
]
},
{
// ...
}
],
"list_id": "9nrwpfj0ou",
"total_items": 4927,
"_links": [
{
// ...
},
// ...
]
}
}
}无论是从外部服务器还是从同一站点连接到 WP REST API,均遵循相同的流程。
例如,以下 GraphQL query 使用 ?context=edit 模式从本地站点连接到 WP REST API(因此必须提供应用程序密码凭据):
query GetPostEditingDataFromRESTAPI(
$postId: ID!,
$username: String!,
$applicationPassword: String!
) {
siteURL: optionValue(name: "siteurl")
@remove
endpoint: _sprintf(
string: "%s/wp-json/wp/v2/posts/%d/?context=edit",
values: [
$__siteURL,
$postId,
]
)
@remove
_sendJSONObjectItemHTTPRequest(input: {
url: $__endpoint,
method: GET,
options: {
auth: {
username: $username,
password: $applicationPassword
}
}
})
}传入以下变量:
{
"postId": 1,
"username": "{username}",
"applicationPassword": "{application password}"
}...响应如下:
{
"data": {
"_sendJSONObjectItemHTTPRequest": {
"id": 1,
"date": "2020-04-17T13:06:58",
"date_gmt": "2020-04-17T13:06:58",
"guid": {
"rendered": "https://mysite.com/?p=1",
"raw": "https://mysite.com/?p=1"
},
"modified": "2020-04-17T13:06:58",
"modified_gmt": "2020-04-17T13:06:58",
"password": "",
"slug": "hello-world",
"status": "publish",
"type": "post",
"link": "https://mysite.com/hello-world/",
"title": {
"raw": "Hello world!",
"rendered": "Hello world!"
},
"content": {
"raw": "<!-- wp:paragraph -->\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>\n<!-- /wp:paragraph -->",
"rendered": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>\n",
"protected": false,
"block_version": 1
},
"excerpt": {
"raw": "",
"rendered": "<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!</p>\n",
"protected": false
},
"author": 2,
"featured_media": 0,
"comment_status": "open",
"ping_status": "open",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
1
],
"tags": [],
"permalink_template": "https://mysite.com/%postname%/",
"generated_slug": "hello-world",
"_links": {
// ...
}
}
}
}连接到 GraphQL API
HTTP Client 还提供了一个函数字段,可方便地连接到 GraphQL API。
字段 _sendGraphQLHTTPRequest 接受 GraphQL 所需的输入(query、变量和操作名称),对提供的端点执行 GraphQL query,并将响应转换为 JSONObject。
以下 query 连接到 GitHub 的 GraphQL API,并获取指定所有者的代码仓库列表:
query FetchGitHubRepositories(
$authorizationToken: String!
$login: String!
$numberRepos: Int! = 3
) {
_sendGraphQLHTTPRequest(input:{
endpoint: "https://api.github.com/graphql",
query: """
query GetRepositoriesByOwner($login: String!, $numberRepos: Int!) {
repositoryOwner(login: $login) {
repositories(first: $numberRepos) {
nodes {
id
name
description
}
}
}
}
""",
variables: [
{
name: "login",
value: $login
},
{
name: "numberRepos",
value: $numberRepos
}
],
options: {
auth: {
password: $authorizationToken
}
}
})
}传入以下 variables:
{
"authorizationToken": "{ GITHUB ACCESS TOKEN }",
"login": "leoloso"
}...将产生如下响应:
{
"data": {
"_sendGraphQLHTTPRequest": {
"data": {
"repositoryOwner": {
"repositories": {
"nodes": [
{
"id": "MDEwOlJlcG9zaXRvcnk2NjcyMTIyNw==",
"name": "PoP",
"description": "Monorepo of the PoP project, including: a server-side component model in PHP, a GraphQL server, a GraphQL API plugin for WordPress, and a website builder"
},
{
"id": "MDEwOlJlcG9zaXRvcnkxODQ1MzE5NzA=",
"name": "PoP-API-WP",
"description": "Bootstrap a PoP API for WordPress"
},
{
"id": "MDEwOlJlcG9zaXRvcnkxOTYwOTk0MzQ=",
"name": "leoloso.com",
"description": "My personal site, based on Hylia (https://hylia.website)"
}
]
}
}
}
}
}
}如果需要重复执行相同的 HTTP 请求,可以使用 @cache 指令(由 Field Resolution Caching 扩展提供)将结果存储到磁盘指定时长,从而加快 query 解析速度。
在 10 秒内执行两次该 query 时(通过 @cache(time:) 参数指定),第二次将获取缓存结果;这将使其更快,因为不需要再连接外部主机:
query ConnectToGitHub($authorizationToken: String!)
{
_sendGraphQLHTTPRequest(input:{
endpoint: "https://api.github.com/graphql",
query: """
{
repositoryOwner(login: "leoloso") {
url
}
}
""",
options: {
auth: {
password: $authorizationToken
}
}
})
# Cache the response to disk, indicating for how many seconds
@cache(time: 10)
}@cache 指令的特性:
- 适用于所有返回 JSON 响应的字段,包括
_sendJSONObjectItemHTTPRequest和_sendGraphQLHTTPRequest - 独立运作(即不依赖所应用字段的逻辑),因此无论 HTTP 请求方法是
GET还是POST均有效 - 不适用于
_sendHTTPRequest,因为其返回的HTTPResponse对象是"瞬态"对象(即不存储在 WordPress 数据库中),仅在当前请求期间存在
从多个 URL 获取数据
我们可以向多个 URL 发送 HTTP 请求,同时获取所有数据。
上面介绍的每个 HTTP 请求字段都有对应的"多个"字段:
_sendHTTPRequests_sendJSONObjectItemHTTPRequests_sendJSONObjectCollectionHTTPRequests_sendGraphQLHTTPRequests
这些字段均有 async 参数,用于指定是异步还是同步执行多个 HTTP 请求:
- 异步:所有 HTTP 请求并行同时执行
- 同步:每个 HTTP 请求仅在前一个请求完成后才发出
以下 GraphQL query 获取多个地区的天气预报数据:
query {
_sendJSONObjectItemHTTPRequests(inputs: [
{
url: "https://api.weather.gov/gridpoints/TOP/31,80/forecast"
},
{
url: "https://api.weather.gov/gridpoints/TOP/41,55/forecast"
}
])
}...产生如下结果:
{
"data": {
"_sendJSONObjectItemHTTPRequests": [
{
"@context": [
"https://geojson.org/geojson-ld/geojson-context.jsonld",
{
"@version": "1.1",
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
}
],
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-97.137207,
39.7444372
],
[
-97.1367549,
39.7223799
],
[
-97.1080809,
39.7227252
],
[
-97.10852700000001,
39.7447825
],
[
-97.137207,
39.7444372
]
]
]
},
"properties": {
"updated": "2023-07-13T05:39:07+00:00",
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2023-07-13T06:44:24+00:00",
"updateTime": "2023-07-13T05:39:07+00:00",
"validTimes": "2023-07-12T23:00:00+00:00/P7DT2H",
"elevation": {
"unitCode": "wmoUnit:m",
"value": 456.8952
},
"periods": [
{
"number": 1,
"name": "Overnight",
"startTime": "2023-07-13T01:00:00-05:00",
"endTime": "2023-07-13T06:00:00-05:00",
"isDaytime": false,
"temperature": 68,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": null
},
"dewpoint": {
"unitCode": "wmoUnit:degC",
"value": 21.666666666666668
},
"relativeHumidity": {
"unitCode": "wmoUnit:percent",
"value": 100
},
"windSpeed": "5 mph",
"windDirection": "NE",
"icon": "https://api.weather.gov/icons/land/night/few?size=medium",
"shortForecast": "Mostly Clear",
"detailedForecast": "Mostly clear, with a low around 68. Northeast wind around 5 mph."
},
{
"number": 2,
"name": "Thursday",
"startTime": "2023-07-13T06:00:00-05:00",
"endTime": "2023-07-13T18:00:00-05:00",
"isDaytime": true,
"temperature": 90,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": null
},
"dewpoint": {
"unitCode": "wmoUnit:degC",
"value": 21.11111111111111
},
"relativeHumidity": {
"unitCode": "wmoUnit:percent",
"value": 100
},
"windSpeed": "5 to 10 mph",
"windDirection": "NE",
"icon": "https://api.weather.gov/icons/land/day/sct?size=medium",
"shortForecast": "Mostly Sunny",
"detailedForecast": "Mostly sunny, with a high near 90. Northeast wind 5 to 10 mph."
},
// ...
]
}
},
{
"@context": [
"https://geojson.org/geojson-ld/geojson-context.jsonld",
{
"@version": "1.1",
"wx": "https://api.weather.gov/ontology#",
"geo": "http://www.opengis.net/ont/geosparql#",
"unit": "http://codes.wmo.int/common/unit/",
"@vocab": "https://api.weather.gov/ontology#"
}
],
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-96.8406778,
39.1956467
],
[
-96.8402904,
39.1735282
],
[
-96.811767,
39.1738261
],
[
-96.8121485,
39.1959446
],
[
-96.8406778,
39.1956467
]
]
]
},
"properties": {
"updated": "2023-07-13T05:39:07+00:00",
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2023-07-13T07:07:02+00:00",
"updateTime": "2023-07-13T05:39:07+00:00",
"validTimes": "2023-07-12T23:00:00+00:00/P7DT2H",
"elevation": {
"unitCode": "wmoUnit:m",
"value": 403.86
},
"periods": [
{
"number": 1,
"name": "Overnight",
"startTime": "2023-07-13T02:00:00-05:00",
"endTime": "2023-07-13T06:00:00-05:00",
"isDaytime": false,
"temperature": 69,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": null
},
"dewpoint": {
"unitCode": "wmoUnit:degC",
"value": 22.22222222222222
},
"relativeHumidity": {
"unitCode": "wmoUnit:percent",
"value": 97
},
"windSpeed": "5 to 10 mph",
"windDirection": "NE",
"icon": "https://api.weather.gov/icons/land/night/few?size=medium",
"shortForecast": "Mostly Clear",
"detailedForecast": "Mostly clear, with a low around 69. Northeast wind 5 to 10 mph."
},
{
"number": 2,
"name": "Thursday",
"startTime": "2023-07-13T06:00:00-05:00",
"endTime": "2023-07-13T18:00:00-05:00",
"isDaytime": true,
"temperature": 93,
"temperatureUnit": "F",
"temperatureTrend": null,
"probabilityOfPrecipitation": {
"unitCode": "wmoUnit:percent",
"value": null
},
"dewpoint": {
"unitCode": "wmoUnit:degC",
"value": 22.22222222222222
},
"relativeHumidity": {
"unitCode": "wmoUnit:percent",
"value": 100
},
"windSpeed": "5 to 10 mph",
"windDirection": "NE",
"icon": "https://api.weather.gov/icons/land/day/sct?size=medium",
"shortForecast": "Mostly Sunny",
"detailedForecast": "Mostly sunny, with a high near 93. Northeast wind 5 to 10 mph."
},
// ...
]
}
}
]
}
}从 API 响应中提取数据
回到 Mailchimp 的 API,让我们从响应中提取所有电子邮件地址的列表。这些地址位于 members 列表中每个条目的 email_address 属性下:
{
"data": {
"_sendJSONObjectItemHTTPRequest": {
"members": [
{
"email_address": "vinesh@yahoo.com",
// ...
},
{
"email_address": "thiago@hotmail.com",
// ...
},
// ...
]
}
}
}Field Value Iteration and Manipulation 扩展提供了可组合指令,用于遍历数组或对象的内部元素,并在这些元素下应用嵌套指令:
@underArrayItem:操作数组中的特定条目@underJSONObjectProperty:操作 JSON 对象中的特定属性@underEachArrayItem:操作数组中的所有条目@underEachJSONObjectProperty:操作 JSON 对象中的所有属性
以下 GraphQL query 导航到每个 email_address 属性,并将其值导出到动态变量 $mailchimpListMemberEmails:
query GetDataFromMailchimp {
mailchimpListMembersJSONObject: _sendJSONObjectItemHTTPRequest(input: {
url: "https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members",
method: GET,
options: {
auth: {
username: "{USER}",
password: "{API_TOKEN}"
}
}
})
@underJSONObjectProperty(by: { key: "members"})
@underEachArrayItem
@underJSONObjectProperty(by: { key: "email_address"})
@export(as: "mailchimpListMemberEmails")
}我们可以通过打印动态变量的值来查看这些条目:
query PrintMailchimpSubscriberEmails
@depends(on: "GetDataFromMailchimp")
{
mailchimpListMemberEmails: _echo(value: $mailchimpListMemberEmails)
}...产生如下结果:
{
"data": {
"mailchimpListMembersJSONObject": {
// ...
},
"mailchimpListMemberEmails": [
"vinesh@yahoo.com",
"thiago@hotmail.com",
// ...
]
}
}请注意,尽管动态变量 $mailchimpListMemberEmails 是一个列表,@export 并没有 type: LIST 参数。
这是因为当 @export 嵌套在 @underEachArrayItem(或 @underEachJSONObjectProperty)下时,导出的值已经是数组。
合并 Mailchimp 订阅者与网站用户数据
假设我们的 Mailchimp 订阅者在网站上也有用户账户,且电子邮件地址是两个应用的共同 ID。
我们便可以使用从 Mailchimp 获取的电子邮件地址(现已存储在动态变量 $mailchimpListMemberEmails 中)来获取网站中存储的对应用户数据:
query GetUsersUsingMailchimpSubscriberEmails
@depends(on: "GetDataFromMailchimp")
{
users(filter: { searchBy: { emails: $mailchimpListMemberEmails } } ) {
id
name
email
}
}响应将为:
{
"data": {
"mailchimpListMembersJSONObject": {
// ...
},
"users": [
{
"id": 88,
"name": "Vinesh Munak",
"email": "vinesh@yahoo.com"
},
{
"id": 705,
"name": "Thiago Barbossa",
"email": "thiago@hotmail.com"
}
]
}
}获取用户后,可以对其应用任何所需的操作(执行 mutation 以更新其数据、发送电子邮件等)。