HTTP Client
Toevoeging van velden aan het GraphQL-schema om HTTP-verzoeken naar een webserver uit te voeren en hun antwoord op te halen:
_sendJSONObjectItemHTTPRequest_sendJSONObjectItemHTTPRequests_sendJSONObjectCollectionHTTPRequest_sendJSONObjectCollectionHTTPRequests_sendHTTPRequest_sendHTTPRequests_sendGraphQLHTTPRequest_sendGraphQLHTTPRequests
Om veiligheidsredenen moeten de URL's waarmee verbinding gemaakt mag worden, expliciet worden geconfigureerd.
Lijst van velden
De volgende velden worden aan het schema toegevoegd.
_sendJSONObjectItemHTTPRequest
Haalt het (REST-)antwoord op voor een enkel JSON-object.
Signature: _sendJSONObjectItemHTTPRequest(input: HTTPRequestInput!): JSONObject.
_sendJSONObjectItemHTTPRequests
Haalt het (REST-)antwoord op voor een enkel JSON-object van meerdere endpoints, uitgevoerd asynchroon (parallel) of synchroon (één na het andere).
Signature: _sendJSONObjectItemHTTPRequests(async: Boolean = true, inputs: [HTTPRequestInput!]!): [JSONObject].
_sendJSONObjectCollectionHTTPRequest
Haalt het (REST-)antwoord op voor een verzameling JSON-objecten.
Signature: _sendJSONObjectCollectionHTTPRequest(input: HTTPRequestInput!): [JSONObject].
_sendJSONObjectCollectionHTTPRequests
Haalt het (REST-)antwoord op voor een verzameling JSON-objecten van meerdere endpoints, uitgevoerd asynchroon (parallel) of synchroon (één na het andere).
Signature: _sendJSONObjectCollectionHTTPRequests(async: Boolean = true, inputs: [HTTPRequestInput!]!): [[JSONObject]].
_sendHTTPRequest
Maakt verbinding met de opgegeven URL en haalt een HTTPResponse-object op, dat de volgende velden bevat:
statusCode: Int!contentType: String!body: String!headers: JSONObject!header(name: String!): StringhasHeader(name: String!): Boolean!
Signature: _sendHTTPRequest(input: HTTPRequestInput!): HTTPResponse.
_sendHTTPRequests
Vergelijkbaar met _sendHTTPRequest, maar ontvangt meerdere URL's en maakt het mogelijk er asynchroon (parallel) verbinding mee te maken.
Signature: _sendHTTPRequests(async: Boolean = true, inputs: [HTTPRequestInput!]!): [HTTPResponse].
_sendGraphQLHTTPRequest
Voert een GraphQL-query uit op het opgegeven endpoint en haalt het antwoord op als een JSON-object.
De invoer van dit veld accepteert de gegevens die voor GraphQL verwacht worden: het endpoint, GraphQL-query, variabelen en de naam van de operatie, en stelt automatisch de standaardmethode (POST) en het contenttype (application/json) in.
Signature: _sendGraphQLHTTPRequest(input: GraphQLRequestInput!): JSONObject.
_sendGraphQLHTTPRequests
Vergelijkbaar met _sendGraphQLHTTPRequests, maar voert meerdere GraphQL-queries tegelijk uit, asynchroon (parallel) of synchroon (één na het andere).
Signature: _sendGraphQLHTTPRequests(async: Boolean = true, inputs: [GraphQLRequestInput!]!): JSONObject.
De toegestane URL's configureren
Je moet de lijst met URL's configureren waarmee je verbinding kunt maken.
Elke vermelding kan ofwel zijn:
- Een regex (reguliere expressie), als die omgeven is door
/of#, of - De volledige URL, anders
Zo komen al deze vermeldingen overeen met de URL "https://gatographql.com/recipes/":
https://gatographql.com/recipes/#https://gatographql.com/recipes/?##https://gatographql.com/.*#/https:\\/\\/gatographql.com\\/(\S+)/
Er zijn 2 plaatsen waar deze configuratie kan worden ingesteld, in volgorde van prioriteit:
- Aangepast: In de bijbehorende Schemaconfiguratie
- Algemeen: Op de instellingenpagina
Selecteer in de Schemaconfiguratie die op het endpoint wordt toegepast de optie "Use custom configuration" en voer de gewenste vermeldingen in:

Anders worden de vermeldingen gebruikt die zijn gedefinieerd op het tabblad "Send HTTP Request Fields" in de Instellingen:

Er zijn 2 gedragingen: "Allow access" en "Deny access":
- Allow access: alleen de geconfigureerde vermeldingen zijn toegankelijk, geen enkele andere
- Deny access: de geconfigureerde vermeldingen zijn niet toegankelijk, alle andere vermeldingen wel

Vereiste bevoegdheid voor toegang tot interne URL's
Sommige URL's verwijzen naar interne adressen (127.0.0.1, link-local-bereiken, cloud-metadata-endpoints, enz.) die interne services kunnen blootstellen als ze bereikt worden. Deze instelling wordt geconfigureerd op de instellingenpagina, onder Plugin Configuration > HTTP Client.

De WordPress-bevoegdheid die de aanvragende gebruiker moet hebben om URL's te kunnen aanspreken die verwijzen naar interne adressen (127.0.0.1, link-local-bereiken, cloud-metadata-endpoints, enz.).
Standaard ingesteld op manage_options zodat niet-beheerders geen toegang kunnen krijgen tot interne services via de HTTP Client-velden.
Selecteer (elke ingelogde gebruiker) om de bevoegdheidscontrole uit te schakelen.
Wanneer gebruik je welk veld
Alle velden lijken op elkaar maar zijn verschillend.
_sendJSONObjectItemHTTPRequest
Dit veld haalt een enkel JSON-object op, wat handig is bij het opvragen van een enkel item van een REST-endpoint, zoals van het WP REST API-endpoint /wp-json/wp/v2/posts/1/.
Deze query:
{
postData: _sendJSONObjectItemHTTPRequest(input: { url: "https://newapi.getpop.org/wp-json/wp/v2/posts/1/" } )
}...haalt dit antwoord op:
{
"data": {
"postData": {
"id": 1,
"date": "2019-08-02T07:53:57",
"date_gmt": "2019-08-02T07:53:57",
"guid": {
"rendered": "https:\/\/newapi.getpop.org\/?p=1"
},
"modified": "2021-01-14T13:18:39",
"modified_gmt": "2021-01-14T13:18:39",
"slug": "hello-world",
"status": "publish",
"type": "post",
"link": "https:\/\/newapi.getpop.org\/uncategorized\/hello-world\/",
"title": {
"rendered": "Hello world!"
},
"content": {
"rendered": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n\n\n\n<p>I’m demonstrating a Youtube video:<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Introduction to the Component-based API by Leonardo Losoviz | JSConf.Asia 2019\" width=\"750\" height=\"422\" src=\"https:\/\/www.youtube.com\/embed\/9pT-q0SSYow?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n<\/div><figcaption>This is my presentation in JSConf Asia 2019<\/figcaption><\/figure>\n",
"protected": false
},
"excerpt": {
"rendered": "<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing! I’m demonstrating a Youtube video:<\/p>\n",
"protected": false
},
"author": 1,
"featured_media": 0,
"comment_status": "closed",
"ping_status": "open",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
1
],
"tags": [
193,
173
]
}
}
}_sendJSONObjectCollectionHTTPRequest
Dit veld is vergelijkbaar met _sendJSONObjectItemHTTPRequest, maar haalt een verzameling JSON-objecten op, zoals van het WP REST API-endpoint /wp-json/wp/v2/posts/.
Deze query:
{
postData: _sendJSONObjectItemHTTPRequest(input: { url: "https://newapi.getpop.org/wp-json/wp/v2/posts/?per_page=3&_fields=id,type,title,date" } )
}...haalt dit antwoord op:
{
"data": {
"postData": [
{
"id": 1692,
"date": "2022-04-26T10:10:08",
"type": "post",
"title": {
"rendered": "My Blogroll"
}
},
{
"id": 1657,
"date": "2020-12-21T08:24:18",
"type": "post",
"title": {
"rendered": "A tale of two cities – teaser"
}
},
{
"id": 1499,
"date": "2019-08-08T02:49:36",
"type": "post",
"title": {
"rendered": "COPE with WordPress: Post demo containing plenty of blocks"
}
}
]
}
}_sendHTTPRequest
Dit veld haalt een HTTPResponse-object op met alle eigenschappen van het antwoord, zodat je onafhankelijk de body (van het type String, dus niet omgezet naar JSON), de statuscode, het contenttype en de headers kunt opvragen.
Zo geeft de volgende query:
{
_sendHTTPRequest(
input: {
url: "https://newapi.getpop.org/wp-json/wp/v2/comments/11/?_fields=id,date,content"
}
) {
statusCode
contentType
headers
body
contentLengthHeader: header(name: "Content-Length")
cacheControlHeader: header(name: "Cache-Control")
}
}...dit antwoord:
{
"data": {
"_sendHTTPRequest": {
"statusCode": 200,
"contentType": "application\/json; charset=UTF-8",
"headers": {
"Access-Control-Allow-Headers": "Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type",
"Access-Control-Expose-Headers": "X-WP-Total, X-WP-TotalPages, Link",
"Allow": "GET",
"Cache-Control": "max-age=300,no-store",
"Content-Length": "508"
},
"body": "{\"id\":11,\"date\":\"2020-12-12T04:09:36\",\"content\":{\"rendered\":\"<p>Wow, this sounds awesome!<\\\/p>\\n\"},\"_links\":{\"self\":[{\"href\":\"https:\\\/\\\/newapi.getpop.org\\\/wp-json\\\/wp\\\/v2\\\/comments\\\/11\"}],\"collection\":[{\"href\":\"https:\\\/\\\/newapi.getpop.org\\\/wp-json\\\/wp\\\/v2\\\/comments\"}],\"author\":[{\"embeddable\":true,\"href\":\"https:\\\/\\\/newapi.getpop.org\\\/wp-json\\\/wp\\\/v2\\\/users\\\/3\"}],\"up\":[{\"embeddable\":true,\"post_type\":\"post\",\"href\":\"https:\\\/\\\/newapi.getpop.org\\\/wp-json\\\/wp\\\/v2\\\/posts\\\/28\"}]}}",
"contentLengthHeader": "508",
"cacheControlHeader": "max-age=300,no-store"
}
}
}_sendGraphQLHTTPRequest
Het uitvoeren van de volgende query:
{
graphQLRequest: _sendGraphQLHTTPRequest(
input: {
endpoint: "https://newapi.getpop.org/api/graphql/"
query: """
query GetPosts($postIDs: [ID]!) {
posts(filter: { ids: $postIDs }) {
id
title
}
}
"""
variables: [
{
name: "postIDs",
value: [1, 1499]
}
]
}
)
}...geeft het volgende antwoord:
{
"data": {
"graphQLRequest": {
"data": {
"posts": [
{
"id": 1499,
"title": "COPE with WordPress: Post demo containing plenty of blocks"
},
{
"id": 1,
"title": "Hello world!"
}
]
}
}
}
}Velden voor meerdere verzoeken: _sendJSONObjectItemHTTPRequests, _sendJSONObjectCollectionHTTPRequests, _sendGraphQLHTTPRequests en _sendHTTPRequests
Deze velden werken vergelijkbaar met hun niet-meervoudige tegenhangers, maar halen gegevens op van meerdere endpoints tegelijk, asynchroon (parallel) of synchroon (één na het andere). De antwoorden worden in een lijst geplaatst, in dezelfde volgorde als de URL's zijn gedefinieerd in de parameter urls.
Zo geeft de volgende query:
{
weatherForecasts: _sendJSONObjectItemHTTPRequests(
urls: [
"https://api.weather.gov/gridpoints/TOP/31,80/forecast",
"https://api.weather.gov/gridpoints/TOP/41,55/forecast"
]
)
}...dit antwoord:
{
"data": {
"weatherForecasts": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-97.1089731,
39.766826299999998
],
[
-97.108526900000001,
39.744778799999999
]
]
]
},
"properties": {
"updated": "2022-03-04T09:39:46+00:00",
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2022-03-04T10:31:47+00:00",
"updateTime": "2022-03-04T09:39:46+00:00",
"validTimes": "2022-03-04T03:00:00+00:00/P7DT22H",
"elevation": {
"unitCode": "wmoUnit:m",
"value": 441.95999999999998
}
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-96.812529900000001,
39.218048000000003
],
[
-96.812148500000006,
39.195940300000004
]
]
]
},
"properties": {
"updated": "2022-03-04T09:39:46+00:00",
"units": "us",
"forecastGenerator": "BaselineForecastGenerator",
"generatedAt": "2022-03-04T10:42:26+00:00",
"updateTime": "2022-03-04T09:39:46+00:00",
"validTimes": "2022-03-04T03:00:00+00:00/P7DT22H",
"elevation": {
"unitCode": "wmoUnit:m",
"value": 409.04160000000002
}
}
}
]
}
}Synchrone vs. asynchrone uitvoering
Deze velden maken het mogelijk meerdere verzoeken uit te voeren:
_sendHTTPRequests_sendJSONObjectItemHTTPRequests_sendJSONObjectCollectionHTTPRequests_sendGraphQLHTTPRequests
Deze velden ontvangen de invoer $async om te bepalen of de verzoeken synchroon ($async => false) of asynchroon uitgevoerd moeten worden.
Synchrone uitvoering
De HTTP-verzoeken worden in volgorde uitgevoerd, waarbij elk verzoek direct na het oplossen van het vorige wordt uitgevoerd.
Wanneer alle HTTP-verzoeken succesvol zijn, drukt het veld een array af met hun antwoorden, in dezelfde volgorde als ze in de invoerlijst staan.
Als een HTTP-verzoek mislukt, stopt de uitvoering op dat punt, d.w.z. de volgende HTTP-verzoeken in de invoerlijst worden niet uitgevoerd.
Enkele mogelijke oorzaken van mislukte HTTP-verzoeken zijn:
- De server waarmee verbinding moet worden gemaakt, is offline
- De statuscode van het antwoord is niet 200: een interne fout 500, een 404 niet gevonden, een 403 verboden, enz.
- Het contenttype van het antwoord is niet
application/json
(De laatste twee worden als een fout behandeld door _sendJSONObjectItemHTTPRequests, _sendJSONObjectCollectionHTTPRequests en _sendGraphQLHTTPRequests, die verwachten alleen JSON-typen te verwerken, maar niet door _sendHTTPRequests, dat geen beperkingen oplegt.)
Bij een fout geeft het veld null terug (d.w.z. het antwoord van eerdere succesvolle HTTP-verzoeken wordt niet afgedrukt), en het foutitem bevat de extensie httpRequestInputArrayPosition om aan te geven welk item uit de invoerlijst is mislukt (beginnend bij 0):
{
"errors": [
{
"message": "Server error: `GET https:\/\/mysite.com\/page-triggering-some-500-error` resulted in a `500 Internal Server Error` response",
"extensions": {
"httpRequestInputArrayPosition": 0,
"field": "_sendJSONObjectItemHTTPRequests(async: false, inputs: [{url: \"https:\/\/mysite.com\/page-triggering-some-500-error\"}, {url: \"https:\/\/mysite.com\/wp-json\/wp\/v2\/posts\/1\/\"}, {url: \"https:\/\/mysite.com\/wp-json\/wp\/v2\/users\/1\/\"}])"
}
}
],
"data": {
"_sendJSONObjectItemHTTPRequests": null
}
}Asynchrone uitvoering
Alle HTTP-verzoeken worden gelijktijdig uitgevoerd (d.w.z. parallel), en het is niet bekend in welke volgorde ze worden opgelost.
Wanneer alle HTTP-verzoeken succesvol zijn, drukt het veld een array af met hun antwoorden, in dezelfde volgorde als ze in de invoerlijst staan.
Zodra een HTTP-verzoek mislukt, stopt de uitvoering onmiddellijk; tegen die tijd kunnen alle andere HTTP-verzoeken echter al uitgevoerd zijn.
Bovendien geeft de server niet aan welk item in de lijst is mislukt (let op: er is geen extensie httpRequestInputArrayPosition in het onderstaande antwoord):
{
"errors": [
{
"message": "Server error: `GET https:\/\/mysite.com\/page-triggering-some-500-error` resulted in a `500 Internal Server Error` response",
"extensions": {
"field": "_sendJSONObjectItemHTTPRequests(async: true, inputs: [{url: \"https:\/\/mysite.com\/page-triggering-some-500-error\"}, {url: \"https:\/\/mysite.com\/wp-json\/wp\/v2\/posts\/1\/\"}, {url: \"https:\/\/mysite.com\/wp-json\/wp\/v2\/users\/1\/\"}])"
}
}
],
"data": {
"_sendJSONObjectItemHTTPRequests": null
}
}Globale velden
Al deze velden zijn Global Fields, dus ze worden aan elk type in het GraphQL-schema toegevoegd: in QueryRoot, maar ook in Post, User, Comment, enz.
Hiermee kun je in dezelfde GraphQL-query verbinding maken met een extern API-endpoint dat tijdens uitvoering wordt gegenereerd, op basis van de gegevens die in een entiteit zijn opgeslagen.
Zo kun je bijvoorbeeld een lijst van gebruikers in je database doorlopen en voor elk van hen verbinding maken met een extern systeem (zoals een CRM) om aanvullende gegevens over hen op te halen.
In deze query genereren we het API-endpoint met behulp van de functie Field to Input en het functieveld _arrayJoin:
{
users(
pagination: { limit: 2 },
sort: { order: ASC, by: ID }
) {
id
endpoint: _arrayJoin(values: [
"https://newapi.getpop.org/wp-json/wp/v2/users/",
$__id,
"?_fields=name"
])
_sendJSONObjectItemHTTPRequest(input: { url: $__endpoint } )
}
}...wat het volgende oplevert:
{
"data": {
"users": [
{
"id": 1,
"endpoint": "https://newapi.getpop.org/wp-json/wp/v2/users/1?_fields=name",
"_sendJSONObjectItemHTTPRequest": {
"name": "leo",
"_links": {
"self": [
{
"href": "https://newapi.getpop.org/wp-json/wp/v2/users/1"
}
],
"collection": [
{
"href": "https://newapi.getpop.org/wp-json/wp/v2/users"
}
]
}
}
},
{
"id": 2,
"endpoint": "https://newapi.getpop.org/wp-json/wp/v2/users/2?_fields=name",
"_sendJSONObjectItemHTTPRequest": {
"name": "themedemos",
"_links": {
"self": [
{
"href": "https://newapi.getpop.org/wp-json/wp/v2/users/2"
}
],
"collection": [
{
"href": "https://newapi.getpop.org/wp-json/wp/v2/users"
}
]
}
}
}
]
}
}