Add-on, ExpressionEngine, Headless, API

Bones

Headless ExpressionEngine With One Line of Template Code.

Buy Now

INSTALLATION

  1. Copy entire bones folder to your system/user/addons folder.
  2. On your EE backend, navigate to Developer > Addons (yoursite.com/admin.php?/cp/addons).
  3. Scroll to Third Party Add-Ons.
  4. Find Bones and click Install.
  5. Add your settings, and enjoy!

SETTINGS

  • Active: If turned on, will return data. When turned off, will return an error.

  • Use API Key: If turned on, each API call will require your API key to be attached to the request.

  • API Key: Add in an API key of your choosing.

USAGE

Reading Entries Via Template Code

Bones is meant to be used within a template all it's own, and is flexible enough to accept all of ExpressionEngine's channel entries tag parameters, either within the template tag itself, or as parameters in your API call.

Create a template group with an index file. For our example, we created api.group with index.html.

If you wanted to retrieve all entries without any filtering, or including filtering within your API call, then you would simply add this to your template:

{exp:bones:entries}

You would then be able to view this in browser (for testing) at your URL and template group, i.e. https://example.com/api

Channel Entry Parameters

Bones automatically accepts all Channel Entries tags parameters as well. So, if you wanted to view only the blog channel with a status of draft, your template code will look like:

{exp:bones:entries channel="blog" status="draft"}

Skipping Standard Object Keys

For entries that are sent back, each one includes a number of standard fields included with each EE entry (see #responses for more information). You can use the following parameters in your call in order to skip those fields:

  • skip_view_count: skip view count data
  • skip_signature: skip signature data
  • skip_avatar: skip avatar data
  • skip_member_data: skip member data

For example:

https://example.com/api?channel=blog&status=draft&skip_view_count=y&skip_member_data=y

Reading Entries Via API Call

You can now use your API by making a GET call to your site as such https://example.com/api

All available channel entries tag parameters are also available via the API as GET or POST variables. So, if you wanted to view only the blog channel with a status of draft, your API Call will look like:

https://example.com/api?channel=blog&status=draft

All query parameters must be properly URL encoded when submitting.

Reading Categories

Categories can be read similarly to entries:

{exp:bones:categories channel="blog"}

Category Parameters

Since EE Category data is only available via a string in the template code, we adjusted this method to get just the categories. Therefore, all of the category parameters in the Channel Categories tag are available except the following:

  • backspace
  • class
  • disable
  • id
  • style

Reading Entries Via API Call

You can now use your API by making a GET call to your site as such https://example.com/api

All available channel categories tag parameters, minus those mentioned above, are also available via the API as GET or POST variables. So, if you wanted to view only the blog channel with a status of draft, your API Call will look like:

https://example.com/api?channel=blog

All query parameters must be properly URL encoded when submitting.

Reading Entries Via Action URL

You can now use your API by making a GET call to your site as such https://example.com/api

Your action IDs are listed in the settings panel of Bones.

https://example.com/ACT=123?channel=blog&status=draft

All query parameters must be properly URL encoded when submitting.

API Key

If you are using an API key to get your data, including the api_key parameter in your calls.

https://example.com/api?api_key=123456789

Since EE search works a bit different in channel parameters, Bones handles this with GET or POST parameters structured as such:

https://example.com/api?search[title]=my+awesome+title

This would be the equivalent of:

{exp:channel:entries search:title="my awesome title"}

All search parameters work the same as the Channel Entries tag, and should be properly url encoded when submitted.

Custom Fields

Out of the box, Bones handles all native EE fields, including Grids, Relationships, and Fluid Fields. It also handles Bloqs, and any custom fields that output text (i.e. WYGWAM).

For other custom fields, you can add your own custom Field Parsers into the FieldFormatters folder. (COMING SOON, let us know what you need)

Response String Only

If you are just looking for quick JSON of your data to use in a template, you can use the response_string=y in the API call in order to get the JSON encoded string for use in your templates.

Security Concerns

Secure API Key

If you are looking for a secure implementation of Bones to a third party app, it is recommended utilizing the API key set up through a backend connector. This allows you to use your API key in other applications without exposing another site and its credentials.

Frontend API Calls

Frontend API calls are a great way to get your data to be slurped on in your frontend javascript. As you do this, ensure you are taking standard API security precautions to protect sensitive data.

It is highly recommend that you should avoid calling data from a secondary domain via the frontend, which may reveal unintended routes from the child site or API credentials, or set up proper CORS for any third party site you would want to call.

RESPONSES

All responses, whether success or error, have 4 incoming parameters:

  • success: Boolean value to indicate whether API call was successful or not
  • message: Any incoming message that would attached to the call. Primarily used for error indication
  • count: Count of incoming data objects. 0 if success is false
  • data: Array of items coming in. Array will be empty if success if false.

Success

ENTRIES: GET https://example.com/api?api_key=123&entry_id=12

{
    "success": true,
    "message": "success",
    "count": 1,
    "data":
    [
        {
            "entry_id": 12,
            "forum_topic_id": null,
            "title": "Entry with vimeo or some junk!!!",
            "url_title": "action-comedy-how-to",
            "status": "open",
            "view_count_one": 0,
            "view_count_two": 0,
            "view_count_three": 0,
            "view_count_four": 0,
            "sticky": "n",
            "entry_date":
            {
                "pretty_date": "Thu, 25 Feb 2021 11:00:00 -0500",
                "seconds_from_epoch": "1614268800",
                "iso8601": "2021-02-25T11:00:00-05:00",
                "raw": 1614268800
            },
            "year": "2021",
            "month": "02",
            "day": "25",
            "edit_date":
            {
                "pretty_date": "Fri, 12 Nov 2021 11:25:23 -0500",
                "seconds_from_epoch": "1636734323",
                "iso8601": "2021-11-12T11:25:23-05:00",
                "raw": 1636734323
            },
            "expiration_date": 0,
            "recent_comment_date": 1614268852,
            "comment_total": 11,
            "channel_title": "Blog",
            "channel_name": "blog",
            "username": "doug",
            "email": "[email protected]",
            "screen_name": "doug",
            "signature": null,
            "sig_img_filename": null,
            "sig_img_width": null,
            "sig_img_height": null,
            "avatar_filename": null,
            "avatar_width": null,
            "avatar_height": null,
            "photo_filename": null,
            "photo_width": null,
            "photo_height": null,
            "site_id": 1,
            "test_fluid_field":
            {
                "about_image":
                [
                    {
                        "image": "https://ee-pro-ee.test/themes/user/site/default/asset/img/common/8yf91jhf3ef71.jpg",
                        "caption": "adadsasd",
                        "align": "right"
                    }
                ],
                "order_billing_address": " 123 Fake St"
            },
            "seo_title": "Action Comedy",
            "seo_desc": "This is how it's done, the incomparable Jackie Chan shows us the way.",
            "related_entries":
            [
                {
                    "entry_id": 7,
                    "title": "Super old entry BUT asdasdaMATION POINTS!!!",
                    "url_title": "super-old-entry",
                    "status": "open",
                    "sticky": false,
                    "entry_date": 1614268800,
                    "edit_date":
                    {
                        "date": "2021-07-27 20:33:11.000000",
                        "timezone_type": 1,
                        "timezone": "+00:00"
                    },
                    "channel_name": null,
                    "username": null,
                    "email": null,
                    "screen_name": null
                },
                {
                    "entry_id": 10,
                    "title": "Entry with SoundCloud audio!!! WHENENNSHSHSHSSHSAOSDJOSDAASDOSDAOSDAJOSJIOSIO",
                    "url_title": "the-one-where-we-shake-it-ff",
                    "status": "open",
                    "sticky": false,
                    "entry_date": 1614268800,
                    "edit_date":
                    {
                        "date": "2021-08-06 13:48:28.000000",
                        "timezone_type": 1,
                        "timezone": "+00:00"
                    },
                    "channel_name": null,
                    "username": null,
                    "email": null,
                    "screen_name": null
                },
                {
                    "entry_id": 13,
                    "title": "Test Page With Everything!!!!",
                    "url_title": "test-page-with-everything",
                    "status": "open",
                    "sticky": false,
                    "entry_date": 1628156040,
                    "edit_date":
                    {
                        "date": "2021-10-04 12:16:14.000000",
                        "timezone_type": 1,
                        "timezone": "+00:00"
                    },
                    "channel_name": null,
                    "username": null,
                    "email": null,
                    "screen_name": null
                }
            ],
            "blog_video":
            [
                {
                    "id": "113439313",
                    "type": "vimeo"
                }
            ],
            "blog_image":
            [],
            "blog_audio":
            [],
            "blog_content": "Lorem ipsum dolor sit amet, <b>this is bold text</b> consectetur <strong>this text is strongly emphasized</strong> adipisicing elit, sed do eiusmod tempor incididunt <i>this is italic text</i> ut labore <em>this text is emphasized</em> et dolore magna aliqua. Ut enim ad minim veniam, <a href=\"\">this is a link</a> quis nostrud <a href=\"\" rel=\"external\">this is an external link</a> laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse <del>This text is deleted</del> <ins>this text is inserted</ins> cillum <code>this is a code sample</code> dolore eu <mark>this text is highlighted</mark> fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\nIOJIJOASDOIJDASJOIASDJOIASDOJASD",
            "rando_date":
            {
                "pretty_date": "Sat, 20 Nov 2021 09:43:56 -0500",
                "seconds_from_epoch": "1637419436",
                "iso8601": "2021-11-20T09:43:56-05:00",
                "raw": null
            },
            "slider_field": {
                "value":49,
                "field_min_value": "0",
                "field_max_value": "100",
                "field_step": "1"
            },
            "duration_field": {
                "value":"984",
                "units":"minutes"
            }
        }
    ]
}

Every entry at a root level will contain the following standard EE fields:

  • entry_id
  • forum_topic_id
  • title
  • url_title
  • status
  • view_count_one
  • view_count_two
  • view_count_three
  • view_count_four
  • sticky
  • entry_date
  • year
  • month
  • day
  • edit_date
  • expiration_date
  • recent_comment_date
  • comment_total
  • channel_title
  • channel_name
  • username
  • email
  • screen_name
  • signature
  • sig_img_filename
  • sig_img_width
  • sig_img_height
  • avatar_filename
  • avatar_width
  • avatar_height
  • photo_filename
  • photo_width
  • photo_height

Entries at any nested level will display:

  • entry_id
  • title
  • url_title
  • status
  • sticky
  • entry_date
  • edit_date
  • channel_name
  • username
  • email
  • screen_name

CATEGORIES: GET https://example.com/api?api_key=123

{
    "success": true,
    "message": "success",
    "count": 5,
    "data":
    [
        {
            "category_name": "News",
            "category_url_title": "news",
            "category_description": null,
            "category_image": "",
            "category_id": 1,
            "parent_id": 0,
            "active": false,
            "count": 1,
            "total_results": 5
        },
        {
            "category_name": "Personal",
            "category_url_title": "personal",
            "category_description": null,
            "category_image": "",
            "category_id": 2,
            "parent_id": 0,
            "active": false,
            "count": 2,
            "total_results": 5
        },
        {
            "category_name": "Photos",
            "category_url_title": "photos",
            "category_description": null,
            "category_image": "",
            "category_id": 3,
            "parent_id": 0,
            "active": false,
            "count": 3,
            "total_results": 5
        },
        {
            "category_name": "Videos",
            "category_url_title": "videos",
            "category_description": null,
            "category_image": "",
            "category_id": 4,
            "parent_id": 0,
            "active": false,
            "count": 4,
            "total_results": 5
        },
        {
            "category_name": "Music",
            "category_url_title": "music",
            "category_description": null,
            "category_image": "",
            "category_id": 5,
            "parent_id": 0,
            "active": false,
            "count": 5,
            "total_results": 5
        }
    ]
}

Errors

Authorization Error

{"success":false,"message":"Not authorized","count":0,"data":[]}

SAMPLES

In the samples folder attached here, you will see a couple of EE templates that will demonstrate usage of Bones:

  • basic-usage: The absolute minimum needed in a template
  • blog-channel-only: Get all entries from a Blog channel

Sample Javascript Implementation

  • Bones Test: In the samples/test.group folder is a template that will fully connect to your API route.
  • bones-hello-world: A simple Vue implementation of the API

TERMS OF SERVICE

Utilizing any API-based service means that your data can be called via a network call. It is of great importance that you use the best API security practices with your implementation of Bones. The creator of this addon and its subsidiaries can not be held responsible for data that is available via API calls through this addon. For more information, or assistance setting up your Bones implementation, please see Support information below.

SUPPORT

We want to make sure you have what you need on this. Email [email protected] for help.