Fetch remote JSON and turn into template tags.
See the YouTube walkthrough
Here is an example using the Random Fox api. Take the JSON response below:
{
"image": "https://randomfox.ca/images/110.jpg",
"link": "https://randomfox.ca/?i=110"
}
EE Template:
{exp:fetchy:get url="https://randomfox.ca/floof/"}
{data}
<a href="{link}">
<img src="{image}" alt="random fox" />
</a>
{/data}
{/exp:fetchy:get}
Output:
<a href="https://randomfox.ca/?i=110">
<img src="https://randomfox.ca/images/110.jpg" alt="random fox" />
</a>
{exp:fetchy:get
url="https://hp-api.onrender.com/api/characters"
cache="true" refresh="500"
headers="x-custom: value, x-more: value"
}
{data limit="10"}
<p>{name}, {house} <i>{wand}({wood}/{core}{/wand})</i></p>
{/data}
{if empty}
<p>No Results.</p>
{/if}
{if error}
<div class="error">
<h3>Error Getting Feed for Harry Potter characters.</h3>
<p>Message: {error_message}</p>
<p>url: {url}</p>
<p>Content Type:
{response_headers}{content_type}{/response_headers}</p>
<pre>{body}</pre>
</div>
{/if}
{/exp:fetchy:get}
Using the SpaceX API https://api.spacexdata.com/v5/launches
{exp:fetchy:get
url="https://api.spacexdata.com/v5/launches/latest"
}
{data}
<article>
<p>{details}</p>
<p>{links}<a href="{webcast}">Livestream Link</a>{/links}</p>
</article>
{/data}
{/exp:fetchy:get}
Arrays that have no named key values can be accessed using the item
tag. In other words, json arrays looking like this:
{
"data": [
"info1",
"info2",
"info3"
]
}
Will be internally converted so that the EE tags look like this:
{data}
{item} <!-- info1... -->
{/data}
:get
TagParameter | Description | Example | Default |
---|---|---|---|
url |
Full path or relative path to a remote data source. Only the “GET” method is supported (at this time…) | url=”https://example.com/data.json” | (empty) |
format |
JSON or XML parsing | format="xml" |
json |
cache |
To use a cached response if exists and not expired. Clear cache cache="clear" |
cache="true" |
false |
refresh |
Time in seconds to cache for. | refresh="300" |
3600 |
headers |
Pass custom headers to the remote URL. Comma-seperated. Useful for bearer tokens. | headers="Token: 1234, X-Stuff: things" |
(*empty) |
debug |
Display the data explorer. Used when building the template tags or debugging. | debug="true" |
false |
prefix |
Append text before each tag name in order to make the tag unique and avoid conflicts elsewhere in the loop. | prefix="fetchy_" |
(empty) |
orderby |
Order by a parameter found in the first level of each item. random is also supported. |
orderby="name" |
none. |
sort |
Reorder the first level of data as asc or desc |
sort="asc" |
The order the API returned the data. |
*Note, the header Content-Type: application/json
is applied by default, and Content-Type: application/xml
if the format is xml
.
These tags are available on every request:
{data}
LoopParameter | Description |
---|---|
{data}{/data} |
The loop that will contain the response data tags. |
count
switch
and total_results
are available in the {data} loop and will have values for data in that loop.Parameter | Description |
---|---|
{ok} |
(boolean) Shortcut if the response was successful. Code 200-299 are true, 300-500 will return false. Internal errors of 0 will also return false. |
{url} |
The url used to make the request. |
{http_code} |
The numeric response code (shortcut to the header field value). |
{http_code_phrase} |
A short phrase of the standard code. |
{http_code_description} |
A longer description fo the standard code. |
{response_headers}{/response_headers} |
Loop. The response headers from the CURL request. The list can be found here. Useful for debugging. |
{response_body} |
Raw text of the response from the remote location. Useful for debugging. |
Parameter | Description |
---|---|
{error} |
(boolean) General error handler covering any errors from connections, decoding, and server response error codes. |
{error_message} |
A human readable error message set by the add-on due to internal error, or when empty is true. |
{internal_error} |
(boolean) Connection issue, url unreachable, and json/xml parsing error. |
{empty} |
(boolean) No text in the response, or the parsed data is empty. Similar to the native no_results |
Template with the Fetchy tags will not load until the remote API responds. PHP is synchronous. This means if the remote API takes 10 seconds to respond, then your users could see a white page for 10 seconds, plus your site’s normal rendering time. Therefore use this add-on with caution.
Caching is available to speed up loads. Caches are saved by the full URL, meaning these two paths would have different caches:
https://example.com/api.php
https://example.com/api.php?
Another way around this blocking process is to call the template over javascript.
For example:
Visitor EE template -> js -> EE template with Fetchy -> php -> Remote API.
The Fetchy template could look like this to output JSON. It uses the free http_header
add-on to set the correct header for returning JSON:
{exp:http_header content_type="application/json"}
{exp:fetchy:get url="xxxx"}
{json}
{/exp:fetchy:get}
data
Parsing BugIf the response uses the word “data” then there is potential for a name bug/conflict in the EE parser. For example, this would create an error:
{data} <!-- Required -->
{data} <!-- The API response -->
{field}
{/data}
{/data}
Therefore the plugin will change the second (returned) data to _data
:
{data}
{_data}
{field}
{/_data}
{/data}
The other solution is using the prefix
parameter, however this would also modify all tags in the loop as well.
There are multiple ways a request can fail, and Fetchy has tags to handle these. The quickest way to jump in is simply to use {error_messsage}
to catch most issues with a general error message, along with {empty}
. Here is an example:
{if error_message}
<p>Something went wrong: {error_message}</p>
{if:elseif empty}
<p>No Results</p>
{if:else}
{data}...{/data}
{/if}
A better way is to be much more descriptive about the problem to your users just by using a few more tags. Errors can be put into two groups:
Setup errors such as connection, and content decoding. Fetchy error handling mostly focuses on setup errors to gracefully fail when connection to remote servers or parsing issues happen.
Remote server errors such as the content not being available, rate limiting, or permission issues.
Errors coming from the remote service will be different for each service and depend on how their API was designed. For example, if a video was deleted, the service should respond with a 404
http status code, along with an error body explaining the error.
Each API is different in terms of how they setup their body data scheme. Each service will take some time to find the best way of handling errors. With Fetchy, good data scheme and error data schemes are just turned into tags, and the service’s error responses would be available in the {data}
tag loop.
A reminder that {empty}
looks at all the returned data. If the data return data includes parameters like { success: true, result: 0 }
then empty
will actually be false
since there is data…its just telling you there no results returned.
Event | {error_message} |
{internal_error} |
{empty} |
{ok} |
{http_code} |
{data} |
---|---|---|---|---|---|---|
Unreachable URL | yes | true |
false |
false |
0 | no |
Parsing Error | yes | true |
false |
true /false |
x > 0 | no |
Non-200 response code | yes | false |
true /false |
false |
> 300 | yes |
Empty response body | empty | false |
true |
true /false |
> 0 | empty |
Sucessful Response | empty | false |
false |
true |
200 | yes |
<!-- Parsing errors, server response code errors, connection errors. -->
{if internal_error}
<p>An error happened. {error_message}</p>
Heres More Debug:
<li>URL: {url}</li>
<li>Code: {http_code}</li>
<li>Body: {response_body}</li>
{/if}
<!-- Non-successful error codes. -->
{if !ok}
<p>The remote server responded with an error.</p>
{data}
//...find the specific API error message.
{/data}
{/if}
<!-- Response was successful -->
{if ok}
<!-- No text returned, or empty array. -->
{if empty}
<p>No Results were returned</p>
{/if}
{data}
//...successful response tags here.
{/data}
{/if}
<!-- Connection Errors -->
{if status_code == '0'}
<p>An error happened. {error_message}</p>
<p>URL: {url}</p>
{/if}
{if status_code == '404'}
<p>Not Found</p>
{/if}
{if status_code == '429'}
<p>Too many requests, please wait and try again.</p>
{/if}
{if status_code == '200'}
<!-- No text returned, or empty array. -->
{if empty}
<p>No Results were returned</p>
{/if}
{data}...{/data}
{/if}
:parse
Tag (bonus)Already have the JSON and just need to parse it? The Parse tag takes json in the body and converts to tags. The {data}
block works exactly the same way as :get
.
Example:
{exp:fetchy:parse}
{json_input}
// Your json as a string.
{/json_input}
{data}
...
{/data}
{/exp:fetchy:parse}
String numbers are converted into integers and need to be prefixed:
{
"123": "data", // Numbers as string.
123: "data", // what is converts to.
"_123": "data" // Will render correctly.
}
If you find a tag not rendering correctly, check these things first:
{error_message}
tags.{data}
loop?{item}
?debug="true"
to see if there is data in the resposne body.If those all check out, grab a copy of the JSON, strip any sensitive info, and send a support request along with the template tag you are trying out.
This add-on requires PHP 8.1 or higher.
Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.