Building a Simple API with ExpressionEngine Templates

UPDATE 1: This article has generated a lot of discussion since it was first published and I just want to reiterate as is stated in the footer that this is not the best way to build an API with Expressionengine. To do that you would build an add-on that could also handle put, post and delete operations. This article is meant only to indroduce the concept of API's and how ExpressionEngine templates can be used to supply the data for them.
UPDATE 2: I have renamed this article. The original title was "Building a RESTful API with ExpressionEngine Templates", however the API described here is not truly RESTful. Thanks to everyone who gave feedback and took part in conversations about this.
API’s are one of the things I love most about the internet. Basically they allow you to create a web app that will communicate with a resource or server and do things for you. And with ExpressionEngine templates it is really easy to create a simple API.
ExpressionEngine templates make outputting content very simple and flexible and for the purpose of this article we will focus on the {exp:channel:entries} tag and build our API on the idea that our website has an "articles" channel that we want to make available to other services. We start by creating a basic template that outputs all entries in our articles channel:
{exp:channel:entries channel="articles" dynamic="no"}
{title}
{/exp:channel:entries}
Let’s assume that this is the "articles" template in a template group called "api". To access it we simply navigate to the domain name followed by "api/articles":
http://www.domain-name.com/api/articles
We now use segments starting with the third segment to define some optional parameters:
Segment 3 = limit
Segment 4 = offset
Segment 5 = display order
And add those to our template tags:
{exp:channel:entries channel="articles" dynamic="no"}
limit="{segment_3}"
offset="{segment_4}"
orderby="{segment_5}"
}
{title}
{/exp:channel:entries}
Now navigating to http://www.domain-name.com/api/articles/3/1/date will output the 3 latest articles offset by 1. The segments are optional as the channel module will ignore parameters that are left blank, so http://www.domain-name.com/api/articles/10 will simply output the 10 latest articles.
Next we want to output the titles of the articles in a way that is computer readable. We could do this in several ways such as with XML, YAML and JSON. I find JSON to be the most human-readable and user-friendly so will use it in this case. All we need to do is create a list of article titles formatted as follows:
[
{ "title" : "My First Article" },
{ "title" : "My Second Article" },
...
]
And we can easily do this in our template as follows:
[
{exp:channel:entries channel="articles" backspace="1" dynamic="no"
limit="{segment_3}"
offset="{segment_4}"
orderby="{segment_5}"
}
{ "title" : "{title}" },
{/exp:channel:entries}
]
The backspace parameter ensures that we don’t have a trailing comma on the end that will break our JSON format.
Navigating to the template now gives us the output in JSON format (you can validate it using JSONLint). Congratulations, you’ve just built an API!!
Let’s create another template called "article" which will get a specific article with the following parameter:
Segment 3 = entry id or url title
And create the template:
[
{exp:channel:entries channel="articles" limit="1"}
{ "title" : "{title}" }
{/exp:channel:entries}
]
Note that because we have not set dynamic to "no" we don’t even need to provide the channel module with any special parameters as by default it is expecting segment_3 to be either the entry id or the url title, and if it is left blank then the latest entry will be shown. So both of these will return the same thing:
http://www.domain-name.com/api/article/85
http://www.domain-name.com/api/article/my_entry_url_title_with_id_85
Let's create one final template called "author" which will get all of the entries by a specific author:
[
{exp:channel:entries channel="articles" backspace="1" dynamic="no"
author_id="{segment_3}"
}
{ "title" : "{title}" },
{/exp:channel:entries}
]
So to get all articles by the author with member id 6 we just navigate to:
http://www.domain-name.com/api/author/6
As you have probably realised by now extending this API to include more parameters, categories, article text, etc. is really very simple. And this really is an API – a specification intended to be used as an interface by software components to communicate with each other. Granted it is not an ideal way to build an API – for that you would really want to build an add-on – however it very nicely demonstrates how API's work and how EE template tags give us all the tools we need to build one.
10 Comments Read below or add one
James Jan 24, at 09:40am
Is it not necessary to set the correct http header?
Ben Croker Jan 24, at 09:54am
hi james. no, it is not necessary. since you know you are expecting JSON you can build it into your app. for example with jQuery you could use:
$.getJSON(‘api/articles’);
if you really need to set the http header then you can do this but would need to add the following php to your template as EE does not provide a JSON template type:
<?php header(‘Content-type: application/json’); ?>
Isaac Jan 28, at 11:46am
What about quotation marks and line returns in your titles or other values once you inevitably add more complex data?
Ben Croker Jan 28, at 11:54am
for more complex data you’ll need to escape quotes and remove line breaks. one option for escaping quotes is the php function addslashes.
for text fields that can contain line breaks we use the Expresso WYSIWYG which has a strip_line_breaks parameter.
Phil Sturgeon Jan 28, at 12:14pm
If this article was called “How to output unescaped JSON in EE” then I’d have nothing to say about this at all, but this is about as RESTful as my shoe and is perpetuating an opinion which is extremely wrong.
How do you handle:
* PUT data
* DELETE links
* Decoding of incoming HTTP body based on Content-Type
* Authentication
* Alternative content-types
* HATEOAS links
* Accept header switching output-types
You might suggest these features are only for “advanced RESTful API’s” but in reality these are the features that make it a RESTful API in the first place. What you have described is nothing more than a single JSON endpoint.
You’ll assume I’m being rude or mean but I’m not, I just like facts.
Ben Croker Jan 31, at 09:35am
hi phil, please keep in mind that the point of this article was only to indroduce the concept of API’s and how ExpressionEngine templates can be used to supply the data for them (obviously the only implemented method is GET). i understand if you found the article title misleading but my intention was not to provide an insight into API’s but simply to show how EE templates can be used to serve up content for them.
Michael C. Jan 28, at 12:57pm
Why use a conditional (which in this case would run every time the entries tag loops except the last time) instead of using the backspace parameter?
Ben Croker Jan 31, at 09:28am
you’re right, i’ve updated the code samples to use the backspace parameter
Isaac Jan 28, at 03:04pm
What about HTML content? What if one of my fields contains JSON data itself?
I just don’t think the approach here, based on templates, is at all a good idea. It would be a lot better to use a plugin and provide content through json_encode().
Ben Croker Jan 31, at 09:37am
Yes, as I said at the end of the article this is far from an ideal way to do this, an add-on would be much better suited for the job. I really just set out to demonstrate how EE templates can serve up content in JSON or other format that be interpreted by API’s.