บทเรียนที่ 19: การดึงข้อมูลจาก API ภายนอก
ส่วนขยาย HTTP Client ช่วยให้เราสามารถส่ง HTTP request ไปยังเว็บเซิร์ฟเวอร์ได้
บทเรียนนี้สาธิตวิธีการดึงข้อมูลจาก API ภายนอก โดย:
- ดึงสมาชิกของอีเมลลิสต์จาก REST API ของ Mailchimp แล้วแยกอีเมลออกมาและนำข้อมูลนี้ไปใช้งานต่อ
- ดึง repository จาก GraphQL API ของ GitHub
การส่ง HTTP request
เอกสารของ API ของ Mailchimp อธิบายว่าเราต้องส่ง request แบบ GET ไปยัง REST API เพื่อดึงข้อมูลสมาชิกของอีเมลลิสต์:
curl --request GET \
--url 'https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members' \
--user 'username:password'มาลองทำสิ่งนี้ด้วย Gato GraphQL กัน
เราส่ง HTTP request ผ่านฟิลด์ global _sendHTTPRequest (ซึ่งจัดเตรียมโดยส่วนขยาย HTTP Client):
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 หลังจากส่งคิวรีแล้ว สังเกตว่าฟิลด์ body (ชนิด String) มีเนื้อหาดิบของ response อยู่:
{
"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 ของ response คือ application/json เราจึงสามารถแปลงเนื้อหาดิบของ body จาก String เป็น JSONObject ผ่านฟิลด์ _strDecodeJSONObject (จากส่วนขยาย PHP Functions Via Schema):
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 ยังจัดเตรียมฟิลด์ฟังก์ชันที่จัดการ response ที่มี content-type application/json ไว้ให้แล้ว ทำให้เหมาะสำหรับการเชื่อมต่อกับ REST API:
_sendJSONObjectItemHTTPRequest: เมื่อเนื้อหาเป็นออบเจกต์ JSON เดียว_sendJSONObjectCollectionHTTPRequest: เมื่อเนื้อหาเป็นคอลเลกชันของออบเจกต์ JSON
ฟิลด์เหล่านี้แปลง response เป็น JSONObject หรือ [JSONObject] ไว้ให้แล้ว
ฟิลด์เหล่านี้คาดหวังว่า status code ของ response จะสำเร็จ (กล่าวคืออยู่ในช่วง 200-299 เช่น 200, 201 หรือ 202) เพราะทำให้สามารถคืนค่า JSONObject ที่มี body ของ response ที่ถอดรหัสเป็น JSON แล้วได้
หากไม่เป็นเช่นนั้น GraphQL response จะมี error ที่เกี่ยวข้องอยู่
ตัวอย่างเช่น เมื่อดึงโพสต์ที่ไม่มีอยู่จริงจากเอนด์พอยต์ /wp-json/wp/v2/posts/{postId}/ ของ WP REST API นั้น response จะเป็น:
{
"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
}
}หากเราไม่ต้องการให้ status code ที่ไม่ใช่ 200s (เช่น 302, 404 หรือ 500) ถูกถือว่าเป็น error เราต้องใช้ฟิลด์ _sendHTTPRequest
เมื่อปรับคิวรีก่อนหน้านี้:
query {
_sendJSONObjectItemHTTPRequest(input: {
url: "https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members",
method: GET,
options: {
auth: {
username: "{USER}",
password: "{API_TOKEN}"
}
}
})
}...จะได้ response ดังนี้:
{
"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 คิวรีนี้เชื่อมต่อกับ WP REST API จากไซต์ภายในเครื่องด้วยโหมด ?context=edit (ซึ่งต้องระบุข้อมูลรับรอง application password):
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}"
}...response จะเป็นดังนี้:
{
"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 รับ input ที่ GraphQL คาดหวัง (คิวรี ตัวแปร และชื่อ operation) ส่ง GraphQL คิวรีไปยังเอนด์พอยต์ที่ระบุ และแปลง response เป็น JSONObject
คิวรีนี้เชื่อมต่อกับ GraphQL API ของ GitHub และดึงรายการ repo ของเจ้าของที่ระบุไว้:
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"
}...จะได้ response ดังนี้:
{
"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 request เดิมซ้ำๆ เราสามารถใช้ directive @cache (จัดเตรียมโดย Field Resolution Caching) เพื่อจัดเก็บผลลัพธ์ลงในดิสก์ตามระยะเวลาที่ร้องขอ ซึ่งช่วยเร่งความเร็วในการ resolve คิวรี
เมื่อส่งคิวรีนี้สองครั้งภายในช่วงเวลา 10 วินาที (ตามที่ระบุผ่านอาร์กิวเมนต์ @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)
}directive @cache:
- ทำงานกับฟิลด์ใดๆ ที่คืนค่า response เป็น JSON รวมถึง
_sendJSONObjectItemHTTPRequestและ_sendGraphQLHTTPRequest - เป็นอิสระ (กล่าวคือไม่สนใจตรรกะของฟิลด์ที่นำไปใช้) ดังนั้นจึงทำงานได้ไม่ว่าเมธอดของ HTTP request จะเป็น
GETหรือPOST - ไม่ทำงานกับ
_sendHTTPRequestเนื่องจากออบเจกต์HTTPResponseที่มันคืนค่าเป็นออบเจกต์แบบ "transient" (กล่าวคือไม่ได้จัดเก็บในฐานข้อมูล WordPress) ซึ่งคงอยู่เฉพาะระหว่าง request ปัจจุบันเท่านั้น
การดึงข้อมูลจากหลาย URL
เราสามารถส่ง HTTP request ไปยังหลาย URL เพื่อดึงข้อมูลจากทั้งหมดในเวลาเดียวกันได้
ฟิลด์ HTTP request แต่ละตัวที่กล่าวถึงข้างต้นมีฟิลด์ "พหูพจน์" ที่สอดคล้องกัน:
_sendHTTPRequests_sendJSONObjectItemHTTPRequests_sendJSONObjectCollectionHTTPRequests_sendGraphQLHTTPRequests
ฟิลด์เหล่านี้ทั้งหมดมีอาร์กิวเมนต์ async เพื่อระบุว่าจะส่ง HTTP request หลายตัวแบบ asynchronous หรือ synchronous:
- แบบ Asynchronous: HTTP request ทั้งหมดถูกส่งพร้อมกันแบบขนาน
- แบบ Synchronous: HTTP request แต่ละตัวจะถูกส่งออกไปหลังจากตัวก่อนหน้าเสร็จสิ้นแล้วเท่านั้น
GraphQL คิวรีนี้ดึงข้อมูลพยากรณ์อากาศสำหรับหลายภูมิภาค:
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."
},
// ...
]
}
}
]
}
}การแยกข้อมูลจาก response ของ API
กลับมาที่ API ของ Mailchimp มาลองแยกรายการที่อยู่อีเมลทั้งหมดออกจาก response กัน ข้อมูลเหล่านี้อยู่ภายใต้พร็อพเพอร์ตี้ email_address ของแต่ละไอเทมในลิสต์ members:
{
"data": {
"_sendJSONObjectItemHTTPRequest": {
"members": [
{
"email_address": "vinesh@yahoo.com",
// ...
},
{
"email_address": "thiago@hotmail.com",
// ...
},
// ...
]
}
}
}ส่วนขยาย Field Value Iteration and Manipulation จัดเตรียม composable directives ที่วนซ้ำผ่านองค์ประกอบภายในของอาร์เรย์หรือออบเจกต์ และนำ directive ที่ซ้อนอยู่ไปใช้กับองค์ประกอบเหล่านั้น:
@underArrayItem: ดำเนินการกับไอเทมที่ระบุจากอาร์เรย์@underJSONObjectProperty: ดำเนินการกับเอนทรีที่ระบุจากออบเจกต์ JSON@underEachArrayItem: ดำเนินการกับทุกไอเทมจากอาร์เรย์@underEachJSONObjectProperty: ดำเนินการกับทุกเอนทรีจากออบเจกต์ JSON
GraphQL คิวรีนี้นำทางไปยังพร็อพเพอร์ตี้ email_address แต่ละตัว และส่งออกค่าของมันไปยังตัวแปร dynamic $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")
}เราสามารถดูเอนทรีเหล่านี้ได้โดยการพิมพ์ค่าของตัวแปร dynamic:
query PrintMailchimpSubscriberEmails
@depends(on: "GetDataFromMailchimp")
{
mailchimpListMemberEmails: _echo(value: $mailchimpListMemberEmails)
}...จะได้:
{
"data": {
"mailchimpListMembersJSONObject": {
// ...
},
"mailchimpListMemberEmails": [
"vinesh@yahoo.com",
"thiago@hotmail.com",
// ...
]
}
}สังเกตว่าแม้ตัวแปร dynamic $mailchimpListMemberEmails จะเป็นลิสต์ แต่ @export ไม่มีอาร์กิวเมนต์ type: LIST
นี่เป็นเพราะเมื่อใดก็ตามที่ @export ถูกซ้อนอยู่ภายใต้ @underEachArrayItem (หรือ @underEachJSONObjectProperty) แล้ว ค่าที่ส่งออกจะเป็นอาร์เรย์อยู่แล้ว
การรวมข้อมูลจากสมาชิก Mailchimp กับผู้ใช้เว็บไซต์
สมมติว่าสมาชิก Mailchimp ของเราก็มีบัญชีผู้ใช้ในเว็บไซต์ของเราด้วย และที่อยู่อีเมลของพวกเขาเป็น ID ร่วมของทั้งสองแอปพลิเคชัน
เราจึงสามารถใช้ที่อยู่อีเมลที่ดึงมาจาก Mailchimp (ซึ่งตอนนี้อยู่ในตัวแปร dynamic $mailchimpListMemberEmails) เพื่อดึงข้อมูลผู้ใช้ที่เกี่ยวข้องซึ่งจัดเก็บอยู่ในไซต์ของเราได้:
query GetUsersUsingMailchimpSubscriberEmails
@depends(on: "GetDataFromMailchimp")
{
users(filter: { searchBy: { emails: $mailchimpListMemberEmails } } ) {
id
name
email
}
}response จะเป็นดังนี้:
{
"data": {
"mailchimpListMembersJSONObject": {
// ...
},
"users": [
{
"id": 88,
"name": "Vinesh Munak",
"email": "vinesh@yahoo.com"
},
{
"id": 705,
"name": "Thiago Barbossa",
"email": "thiago@hotmail.com"
}
]
}
}เมื่อดึงผู้ใช้มาแล้ว เราสามารถดำเนินการใดๆ ที่ต้องการกับพวกเขาได้ (ส่ง mutation เพื่ออัปเดตข้อมูล ส่งอีเมล ฯลฯ)