Lección 19: Recuperando datos desde una API externa
La extensión HTTP Client nos permite ejecutar peticiones HTTP contra un servidor web.
Esta lección del tutorial demuestra cómo obtener datos desde una API externa, mediante:
- Recuperar los miembros de una lista de email desde la API REST de Mailchimp, extraer sus emails, y hacer algo con estos datos
- Recuperar repositorios desde la API GraphQL de GitHub
Ejecutando una petición HTTP
Los docs para la API de Mailchimp explican que debemos enviar una petición GET contra la API REST, para obtener los datos de los miembros de una lista de email:
curl --request GET \
--url 'https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members' \
--user 'username:password'Repliquemos esto en Gato GraphQL.
Ejecutamos una petición HTTP mediante el campo global _sendHTTPRequest (proporcionado por la extensión 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")
}
}El campo _sendHTTPRequest devuelve un objeto de tipo HTTPResponse. Tras ejecutar la consulta, nótese que el campo body (de tipo String) contiene el contenido en bruto de la respuesta:
{
"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"
}
}
}Como el content-type de la respuesta es application/json, podemos transformar el contenido en bruto del body de String a JSONObject mediante el campo _strDecodeJSONObject (de la extensión 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)
}
}El body ahora es accesible como un objeto 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": [
{
// ...
},
// ...
]
}
}
}
}Conectándose a una API REST
HTTP Client también proporciona campos de función que ya manejan respuestas de content-type application/json, haciéndolos adecuados para conectarse a APIs REST:
_sendJSONObjectItemHTTPRequest: Cuando el contenido pertenece a un único objeto JSON_sendJSONObjectCollectionHTTPRequest: Cuando el contenido pertenece a una colección de objetos JSON
Estos campos ya convierten la respuesta a JSONObject o [JSONObject].
Estos campos esperan que el código de estado de la respuesta sea exitoso (es decir, en el rango 200-299, como 200, 201 o 202), ya que les permite devolver ya un JSONObject que contiene el body de la respuesta decodificado como JSON.
Cuando este no es el caso, la respuesta GraphQL contendrá un error correspondiente.
Por ejemplo, al obtener una entrada inexistente desde el endpoint /wp-json/wp/v2/posts/{postId}/ de la API REST de WP, la respuesta será:
{
"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
}
}Si no queremos tratar ningún código de estado distinto de 200s (como 302, 404 o 500) como un error, debemos usar el campo _sendHTTPRequest.
Adaptando la consulta anterior:
query {
_sendJSONObjectItemHTTPRequest(input: {
url: "https://us7.api.mailchimp.com/3.0/lists/{LIST_ID}/members",
method: GET,
options: {
auth: {
username: "{USER}",
password: "{API_TOKEN}"
}
}
})
}...produce esta respuesta:
{
"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": [
{
// ...
},
// ...
]
}
}
}Conectarse a la API REST de WP, ya sea desde un servidor externo o desde este mismo sitio, sigue el mismo procedimiento.
Por ejemplo, esta consulta GraphQL se conecta a la API REST de WP desde el sitio local con el modo ?context=edit (para lo cual debe proporcionar las credenciales 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
}
}
})
}Pasando estas variables:
{
"postId": 1,
"username": "{username}",
"applicationPassword": "{application password}"
}...la respuesta es:
{
"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": {
// ...
}
}
}
}Conectándose a una API GraphQL
HTTP Client también proporciona un campo de función para conectarse cómodamente a APIs GraphQL.
El campo _sendGraphQLHTTPRequest acepta las entradas esperadas por GraphQL (la consulta, variables y nombre de la operación), ejecuta la consulta GraphQL contra el endpoint proporcionado, y convierte la respuesta a JSONObject.
Esta consulta se conecta a la API GraphQL de GitHub y recupera la lista de repos para el propietario indicado:
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
}
}
})
}Pasando estas variables:
{
"authorizationToken": "{ GITHUB ACCESS TOKEN }",
"login": "leoloso"
}...produce esta respuesta:
{
"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)"
}
]
}
}
}
}
}
}Si debemos ejecutar la misma petición HTTP repetidamente, podemos usar la directiva @cache (proporcionada por la extensión Cacheo de Resolución de Campos) para almacenar el resultado en disco durante una cantidad de tiempo solicitada, acelerando así la resolución de la consulta.
Al ejecutar esta consulta dos veces dentro de un lapso de 10 segundos (como se indica mediante el argumento @cache(time:)), la segunda vez recuperará el resultado en caché; esto la hará más rápida, ya que no se conectará al host externo:
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)
}La directiva @cache:
- Funciona con cualquiera de los campos que devuelven una respuesta JSON, incluyendo
_sendJSONObjectItemHTTPRequesty_sendGraphQLHTTPRequest - Es independiente (es decir, no le importa la lógica de los campos donde se aplica), por lo que funciona ya sea que el método de la petición HTTP sea
GEToPOST - No funciona con
_sendHTTPRequest, ya que el objetoHTTPResponseque devuelve es un objeto "transitorio" (es decir, no se almacena en la base de datos de WordPress), que solo vive durante la petición actual
Obteniendo datos desde múltiples URLs
Podemos enviar peticiones HTTP a múltiples URLs, obteniendo datos de todas ellas al mismo tiempo.
Cada uno de los campos de petición HTTP explorados arriba tiene un campo "múltiple" correspondiente:
_sendHTTPRequests_sendJSONObjectItemHTTPRequests_sendJSONObjectCollectionHTTPRequests_sendGraphQLHTTPRequests
Todos estos campos tienen el argumento async, para indicar si ejecutar las múltiples peticiones HTTP de forma asíncrona o síncrona:
- Asíncrona: Las peticiones HTTP se ejecutan todas juntas, en paralelo
- Síncrona: Cada petición HTTP se envía solo después de que la anterior se haya completado
Esta consulta GraphQL recupera datos de previsión meteorológica para múltiples regiones:
query {
_sendJSONObjectItemHTTPRequests(inputs: [
{
url: "https://api.weather.gov/gridpoints/TOP/31,80/forecast"
},
{
url: "https://api.weather.gov/gridpoints/TOP/41,55/forecast"
}
])
}...produciendo:
{
"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."
},
// ...
]
}
}
]
}
}Extrayendo datos desde la respuesta de la API
Volviendo a la API de Mailchimp, extraigamos la lista de todas las direcciones de email desde la respuesta. Estas están contenidas bajo la propiedad email_address en cada elemento de la lista members:
{
"data": {
"_sendJSONObjectItemHTTPRequest": {
"members": [
{
"email_address": "vinesh@yahoo.com",
// ...
},
{
"email_address": "thiago@hotmail.com",
// ...
},
// ...
]
}
}
}La extensión Iteración y Manipulación del Valor de Campos proporciona directivas componibles que iteran sobre los elementos internos de arrays u objetos, y aplican su(s) directiva(s) anidada(s) bajo esos elementos:
@underArrayItem: Opera sobre un elemento específico del array@underJSONObjectProperty: Opera sobre una entrada específica del objeto JSON@underEachArrayItem: Opera sobre todos los elementos del array@underEachJSONObjectProperty: Opera sobre todas las entradas del objeto JSON
Esta consulta GraphQL navega a cada una de las propiedades email_address, y exporta su valor a la variable dinámica $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")
}Podemos visualizar las entradas imprimiendo el valor de la variable dinámica:
query PrintMailchimpSubscriberEmails
@depends(on: "GetDataFromMailchimp")
{
mailchimpListMemberEmails: _echo(value: $mailchimpListMemberEmails)
}...produciendo:
{
"data": {
"mailchimpListMembersJSONObject": {
// ...
},
"mailchimpListMemberEmails": [
"vinesh@yahoo.com",
"thiago@hotmail.com",
// ...
]
}
}Nótese que, aunque la variable dinámica $mailchimpListMemberEmails es una lista, @export no tiene el argumento type: LIST.
Esto es porque cuando @export está anidado debajo de @underEachArrayItem (o @underEachJSONObjectProperty), el valor exportado ya será un array.
Combinando datos de suscriptores de Mailchimp y usuarios del sitio
Supongamos que nuestros suscriptores de Mailchimp también tienen un usuario en nuestro sitio web, y que su dirección de email es el ID común para ambas aplicaciones.
Podemos entonces usar las direcciones de email recuperadas desde Mailchimp (ahora colocadas bajo la variable dinámica $mailchimpListMemberEmails) para obtener los datos de usuario correspondientes almacenados en nuestro sitio:
query GetUsersUsingMailchimpSubscriberEmails
@depends(on: "GetDataFromMailchimp")
{
users(filter: { searchBy: { emails: $mailchimpListMemberEmails } } ) {
id
name
email
}
}La respuesta será:
{
"data": {
"mailchimpListMembersJSONObject": {
// ...
},
"users": [
{
"id": 88,
"name": "Vinesh Munak",
"email": "vinesh@yahoo.com"
},
{
"id": 705,
"name": "Thiago Barbossa",
"email": "thiago@hotmail.com"
}
]
}
}Una vez recuperados los usuarios, podemos aplicar cualquier operación deseada sobre ellos (ejecutar una mutación para actualizar sus datos, enviar un email, etc.).