Four Kitchens
Insights

REST easy, part 3: Now filter this

8 Min. ReadDevelopment

In our first two tutorials we saw how easy it was to create a REST API endpoint for a Drupal content type of custom fields using the powerful RESTful module. The RESTful module provides some great features for API consumers, too.

The module builds on the filtering technology of the Entity module through Entity Field Query. There are some great posts on the topic if you’re looking for more background. Basically, the entity module wraps a dynamic querying class that has some awareness to how fields are attached to entities and creates an easily extendable interface for filtering those entities through those fields and that entities’ properties.

OOP, there it is

As I mentioned in the first tutorial the internals of the RESTful module are all PHP OOP. In the first and second tutorial we created classes that were used for the RESTful ctools plugins that we defined. Those classes were subclasses of the REST entity class: class ResteasyRestfulEntityArtistsResource extends RestfulEntityBaseNode. In tu\r\n \the RestfulEntityBaseNode extends other classes and implements an interface related to Entity Field Query (EFQ) class RestfulEntityBase extends RestfulDataProviderEFQ implements RestfulEntityInterface and it is in this way that the power of EFQ makes its way down to your endpoints.

While I didn’t design the module, I believe the basic main idea behind all of this inheritance that I’ve just outlined is to answer the question – what kind of data is this (an entity) and how am I going to get it (EFQ)? As the inheritance moves further on the chain it becomes more specific (this entity is a node) and finally in the classes we defined in our tutorial (this bundle with these fields).

What does this mean?

So what does it mean to have EFQ backing up our node-based endpoints by default? Well, it means you will be able to do the following without much trouble:

  • Filter endpoints on fields by their value: Show me all results from the U.K.
  • Filter endpoints by ranges of field values: Show me all results from 1955-1966
  • Apply basic comparison operators to your field filters like <,>,=,<=,>=: Show me results that are before 1990.
  • Apply compound filters, tying together different values and operations on a single field with multiple values: Show me all results from the U.K. that are before 1990.

That is some pretty powerful filtering functionality to accompany your endpoints. As a developer you will appreciate having a reliable framework for endpoint querying if you are placed in charge of developing a front-end for your headless site or for supporting your API consumers in their desires to query your API endpoints. Plus, since the RESTful module provides a uniform filtering approach, you and your API consumers will have many resources including blog posts like this one, the RESTful module’s docs and your own API docs (perhaps in a service like Apiary or the like).

Taking care of Business

Let’s get down to filtering. We will use the endpoint we created in our first two tutorials, a simple artist endpoint to show some of our basic filtering techniques.

First off let’s make a simple call to our endpoint with no filters. (NOTE: For all of your home players — this is a GET request).

/api/v0.2/artists

Which gives us the following results:

{
    "data":[
        {
            "id":1,
            "label":"The Beatles",
            "self":"http://resteasy.local.dev/api/v0.2/artists/1",
            "yearFormed":"1960",
            "countryOfOrigin":"England",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        },
        {
            "id":2,
            "label":"Chuck Berry",
            "self":"http://resteasy.local.dev/api/v0.2/artists/2",
            "yearFormed":"1955",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        },
        {
            "id":3,
            "label":"The Jimi Hendrix Experience",
            "self":"http://resteasy.local.dev/api/v0.2/artists/3",
            "yearFormed":"1966",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":"http://jimihendrix.com"
        },
        {
            "id":4,
            "label":"My Bloody Valentine",
            "self":"http://resteasy.local.dev/api/v0.2/artists/4",
            "yearFormed":"1983",
            "countryOfOrigin":"Ireland",
            "rockAndRollHallOfFameInductee":false,
            "description":"u003Cpu003EMy Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983. Since 1987, the band\u0027s lineup has consisted of founding members Kevin Shields (vocals, guitar) and Colm u00d3 Cu00edosu00f3ig (drums), with Bilinda Butcher (vocals, guitar) and Debbie Googe (bass). The group is known for their integration of distorted noise and ethereal melody, and for their innovative utilization of unorthodox guitar and production techniques. Their work in the late 1980s and early 1990s resulted in their pioneering a musical style known as shoegazing.u003C/pu003Enu003Cpu003E(from u003Ca href=\u0022https://en.wikipedia.org/wiki/My_Bloody_Valentine_%28band%29\u0022u003Ehttps://en.wikipedia.org/wiki/My_Bloody_Valentine_%28band%29u003C/au003E)u003C/pu003En",
            "artistWebsite":null
        }
    ],
    "count":4,
    "self":{
        "title":"Self",
        "href":"http://resteasy.local.dev/api/v0.2/artists"
    }
}

Filtering by value on a boolean field

Now let’s add a basic value filter. Filters in RESTful take a little bit of getting used to – but once you get the hang of it – they all work very similarly. Each time you add a filter to your API call you want to begin with the following: filter[{the public field you are filtering by}][value]={value}. The public field in the first set of brackets relates to the public fields you defined in your endpoint that you defined. So let’s add our very first filter to our API call:

api/v0.2/artists?filter[rockAndRollHallOfFameInductee][value]=0

In this call we will filter the public field rockAndRollHallOfFameInductee for a value of 0 which should give us the results of any artist who was not inducted into the hall of fame. Boolean values are expressed in filters in their raw, 0 or 1 format, even though it is presented as true or false. This gives us the following results:

{
    "data":[
        {
            "id":4,
            "label":"My Bloody Valentine",
            "self":"http://resteasy.local.dev/api/v0.2/artists/4",
            "yearFormed":"1983",
            "countryOfOrigin":"Ireland",
            "rockAndRollHallOfFameInductee":false,
            "description":"u003Cpu003EMy Bloody Valentine are an alternative rock band formed in Dublin, Ireland in 1983. Since 1987, the band\u0027s lineup has consisted of founding members Kevin Shields (vocals, guitar) and Colm u00d3 Cu00edosu00f3ig (drums), with Bilinda Butcher (vocals, guitar) and Debbie Googe (bass). The group is known for their integration of distorted noise and ethereal melody, and for their innovative utilization of unorthodox guitar and production techniques. Their work in the late 1980s and early 1990s resulted in their pioneering a musical style known as shoegazing.u003C/pu003Enu003Cpu003E(from u003Ca href=\u0022https://en.wikipedia.org/wiki/My_Bloody_Valentine_%28band%29\u0022u003Ehttps://en.wikipedia.org/wiki/My_Bloody_Valentine_%28band%29u003C/au003E)u003C/pu003En",
            "artistWebsite":null
        }
    ],
    "count":1,
    "self":{
        "title":"Self",
        "href":"http://resteasy.local.dev/api/v0.2/artists"
    }
}

Which is exactly right for my current data set. Now, we can use this same format for any field.

Filtering by value on a text field

Let’s try it for country of origin, a text value. So we’ll simply replace the value for the field name in the first set of brackets and add the new value we want to filter on.

api/v0.2/artists?filter[countryOfOrigin][value]=usa

Which gives us the following results:

{
    "data":[
        {
            "id":2,
            "label":"Chuck Berry",
            "self":"http://resteasy.local.dev/api/v0.2/artists/2",
            "yearFormed":"1955",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        },
        {
            "id":3,
            "label":"The Jimi Hendrix Experience",
            "self":"http://resteasy.local.dev/api/v0.2/artists/3",
            "yearFormed":"1966",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":"http://jimihendrix.com"
        }
    ],
    "count":2,
    "self":{
        "title":"Self",
        "href":"http://resteasy.local.dev/api/v0.2/artists"
    }
}

Success, once again.

Filtering with different operators.

Let’s try using another operator than “=” to filter on our number field for year. The RESTful module makes it easy to use the following operators:

  • Greater than: >
  • Less than: <
  • Equal to: =
  • Greater than or equal to: >=
  • Less than or equal to: <=

Let’s get all of the artists who started their careers before 1970. When you add an operator to your filter you need to add the following in addition to the value parameter &filter[{public field name}][operator]="{operator}". So let’s take a look at them together (Note: double quotes will be url encoded):

api/v0.2/artists?filter[yearFormed][value]=1970&filter[yearFormed][operator]="<"

Which gives us the following results:

{
    "data":[
        {
            "id":1,
            "label":"The Beatles",
            "self":"http://resteasy.local.dev/api/v0.2/artists/1",
            "yearFormed":"1960",
            "countryOfOrigin":"England",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        },
        {
            "id":2,
            "label":"Chuck Berry",
            "self":"http://resteasy.local.dev/api/v0.2/artists/2",
            "yearFormed":"1955",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        },
        {
            "id":3,
            "label":"The Jimi Hendrix Experience",
            "self":"http://resteasy.local.dev/api/v0.2/artists/3",
            "yearFormed":"1966",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":"http://jimihendrix.com"
        }
    ],
    "count":3,
    "self":{
        "title":"Self",
        "href":"http://resteasy.local.dev/api/v0.2/artists"
    }
}

Compound filtering on a single field – filter by range

Now let’s try combining filters on a single field. This could be useful to you or your consumers trying to filter out multiple values from a field, or for, in this instance, filtering for a range of entities. When you are applying more than one filter to a endpoint call you will need to mimic an array item to tie the filter and the value together. A format that can be described as filter[{publicfieldname}][value][{instance number}]=1950. That looks like this in real life:

filter[yearFormed][value][0]=1950

So let’s put that together and query our API for artists that originated after 1950 and on or before 1960. Note: Quotes have been url escaped.

api/v0.2/artists?filter[yearFormed][value][0]=1950&filter[yearFormed][operator][0]=">"&filter[yearFormed][value][1]=1960&filter[yearFormed][operator][1]="<="

Which gives us the following results:

{
    "data":[
        {
            "id":1,
            "label":"The Beatles",
            "self":"http://resteasy.local.dev/api/v0.2/artists/1",
            "yearFormed":"1960",
            "countryOfOrigin":"England",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        },
        {
            "id":2,
            "label":"Chuck Berry",
            "self":"http://resteasy.local.dev/api/v0.2/artists/2",
            "yearFormed":"1955",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        }
    ],
    "count":2,
    "self":{
        "title":"Self",
        "href":"http://resteasy.local.dev/api/v0.2/artists"
    }
}

Filtering multiple fields at the same time

Finally let’s add another filter on a different field to show that we can compound filters. Here let’s ask for the same date range filter as above but only return results of artists from the USA.

api/v0.2/artists?filter[yearFormed][value][0]=1950&filter[yearFormed][operator][0]=">"&filter[yearFormed][value][1]=1960&filter[yearFormed][operator][1]="<="&filter[countryOfOrigin][value]=usa
{
    "data":[
        {
            "id":2,
            "label":"Chuck Berry",
            "self":"http://resteasy.local.dev/api/v0.2/artists/2",
            "yearFormed":"1955",
            "countryOfOrigin":"USA",
            "rockAndRollHallOfFameInductee":true,
            "description":null,
            "artistWebsite":null
        }
    ],
    "count":1,
    "self":{
        "title":"Self",
        "href":"http://resteasy.local.dev/api/v0.2/artists"
    }
}

This is just the start, as this filtering technology can be applied to date fields, boolean, numbers, and text fields, just to name a few. In this tutorial we used filtering provided by the RESTful module to:

  • Filter our custom API node endpoint by value
  • Filter our custom API node endpoint by a range of values
  • Filter our custom API node endpoint on multiple filters on a single field
  • Filter our custom API node endpoint with basic comparison operators (=,<,>,<=,>=)

Now you have the power to filter your API endpoints to your heart’s content. Stick around though, next time we’ll dig back into the php code of the RESTful module and add an entirely new kind of endpoint to our custom API.


Read more of the REST easy series: