Playing around with the Drupal 8 JSON API as a data service

I’m familiar with the concepts of REST but have never done anything in practice before. I decided I wanted to end that and start messing with the JSON API in Drupal 8. It was surprisingly easy to get going with because of the incredible resources out there. In this multi-part series, I’m going to explore how to use Drupal 8 as a data back-end for a React app.

JSON API Drupal Playlist by Mateu Aguiló Bosch

This video series is an excellent job of succinctly explaining how the JSON API can be used to make basic requests to Drupal.

Postman is amazing

In the series, Mateu is using a tool called Postman (free!), that makes managing REST requests really easy. This is amazing for testing out your API before you hook it up to an application.

Using Postman, for requests that require authentication, you can set up basic authentication (which uses your Drupal username and password) for easy testing purposes.

The examples below were all run with Postman. I decided it would most likely be useful to someone if I showed how I POSTed data to a content type using the JSON API.

Potential gotcha
Make sure in your request headers, you are specifying the Content-Type as application/vnd.api+json. This is according to the JSONAPI spec. Not doing so will throw a 422 Response from the server.

My app use case

I’m building a workout routine React application. The main idea is that users have Routines which contain Timed Tasks (could be yoga postures, jumping jacks, whatever). The Routines hold references to the Timed Tasks. The user can create a Routine of Timed Tasks, and then “play” through the routine, which will prompt the user to perform a workout action.

Timed Task content type

  • Title
  • Body
  • Task Setup
    • Minutes
    • Seconds
  • Task Duration
    • Minutes
    • Seconds
  • Task Rest
    • Minutes
    • Seconds

Routine content type

  • Title
  • Body
  • Timed Task entity reference

Now, lets use Postman to test some basic REST calls that I’m sure my React app will have to make.

Examples:

Local site url: http://drupal-8-3-0.dd:8083

Adding TimedTask content

POST -> http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task

Body

{
   "data":{
      "type":"node--timed_task",
      "attributes":{
         "status":true,
         "title":"Half Moon right side",
         "body":{
            "value":"This pose is tough",
            "format":"basic_html"
         },
         "field_setup_minutes":{
            "value":0
         },
         "field_setup_seconds":{
            "value":30
         },
         "field_task_minutes":{
            "value":1
         },
         "field_task_seconds":{
            "value":0
         },
         "field_rest_minutes":{
            "value":0
         },
         "field_rest_seconds":{
            "value":20
         }
      }
   }
}

Response

201 Created

{
  "data": {
    "type": "node--timed_task",
    "id": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
    "attributes": {
      "nid": 56,
      "uuid": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
      "vid": 56,
      "langcode": "en",
      "status": true,
      "title": "Half Moon right side",
      "created": 1491878234,
      "changed": 1491878234,
      "promote": false,
      "sticky": false,
      "revision_timestamp": 1491878234,
      "revision_log": null,
      "revision_translation_affected": true,
      "default_langcode": true,
      "content_translation_source": "und",
      "content_translation_outdated": false,
      "path": null,
      "body": {
        "value": "This pose is tough",
        "format": "basic_html",
        "summary": null
      },
      "field_rest_minutes": 0,
      "field_rest_seconds": 20,
      "field_setup_minutes": 0,
      "field_setup_seconds": 30,
      "field_task_minutes": 1,
      "field_task_seconds": 0
    },
    "relationships": {
      "type": {
        "data": {
          "type": "node_type--node_type",
          "id": "d1dd56a7-af44-4c01-931d-b441da7f40dc"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/type",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/type"
        }
      },
      "uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/uid"
        }
      },
      "revision_uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/revision_uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/revision_uid"
        }
      }
    },
    "links": {
      "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908"
    }
  },
  "links": {
    "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task"
  }
}

There’s a lot of stuff in there that we don’t need, but this piece is of interest:

"attributes": {
  "nid": 56,
  "uuid": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
  "vid": 56,
  "langcode": "en",
  "status": true,
  "title": "Half Moon right side",
  "created": 1491878234,
  "changed": 1491878234,
  "promote": false,
  "sticky": false,
  "revision_timestamp": 1491878234,
  "revision_log": null,
  "revision_translation_affected": true,
  "default_langcode": true,
  "content_translation_source": "und",
  "content_translation_outdated": false,
  "path": null,
  "body": {
    "value": "This pose is tough",
    "format": "basic_html",
    "summary": null
  },
  "field_rest_minutes": 0,
  "field_rest_seconds": 20,
  "field_setup_minutes": 0,
  "field_setup_seconds": 30,
  "field_task_minutes": 1,
  "field_task_seconds": 0
}

The core of the TimedTask is structured nicely for me to use in a front end framework like React.

Now, I want to be able to store a collection of these TimedTasks in a collection known as a Routine. A Routine will consist of many TimedTasks that can be rearranged.

I added 3 more Timed Tasks and noted their UUIDs in the response.

  • ad4a8609-1b98-436c-9efd-fc9fe91ad908
  • 390de987-11b2-47b4-a692-3c160bccab0a
  • 760a6c82-b0f1-4b2a-93d4-140061e878a9
  • 6548f256-a518-491a-8bba-d469778a3722

Now, lets add these as entity references to a Routine content type.

POST -> http://drupal-8-3-0.dd:8083/jsonapi/node/routine

Body

{
   "data":{
      "type":"node--routine",
      "attributes":{
         "langcode":"en",
         "status":true,
         "title":"My Yoga Routine :)",
         "body":{
            "value":"<p>This is my morning workout :D</p>\r\n",
            "format":"basic_html",
            "summary":""
         }
      },
      "relationships":{
         "field_timedtasks":{
            "data":[
               {
                  "type":"node--timed_task",
                  "id":"ad4a8609-1b98-436c-9efd-fc9fe91ad908"
               },
               {
                  "type":"node--timed_task",
                  "id":"390de987-11b2-47b4-a692-3c160bccab0a"
               },
               {
                  "type":"node--timed_task",
                  "id":"760a6c82-b0f1-4b2a-93d4-140061e878a9"
               },
               {
                  "type":"node--timed_task",
                  "id":"6548f256-a518-491a-8bba-d469778a3722"
               }
            ]
         }
      }
   }
}

Response

201 Created

{
  "data": {
    "type": "node--routine",
    "id": "5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1",
    "attributes": {
      "nid": 62,
      "uuid": "5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1",
      "vid": 62,
      "langcode": "en",
      "status": true,
      "title": "My Yoga Routine :)",
      "created": 1491879173,
      "changed": 1491879173,
      "promote": false,
      "sticky": false,
      "revision_timestamp": 1491879173,
      "revision_log": null,
      "revision_translation_affected": true,
      "default_langcode": true,
      "content_translation_source": "und",
      "content_translation_outdated": false,
      "path": null,
      "body": {
        "value": "<p>This is my morning workout :D</p>\r\n",
        "format": "basic_html",
        "summary": ""
      }
    },
    "relationships": {
      "type": {
        "data": {
          "type": "node_type--node_type",
          "id": "d50564ec-fb5e-4031-866a-38e8ca98dc0c"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/type",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/type"
        }
      },
      "uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/uid"
        }
      },
      "revision_uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/revision_uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/revision_uid"
        }
      },
      "field_timedtasks": {
        "data": [
          {
            "type": "node--timed_task",
            "id": "ad4a8609-1b98-436c-9efd-fc9fe91ad908"
          },
          {
            "type": "node--timed_task",
            "id": "390de987-11b2-47b4-a692-3c160bccab0a"
          },
          {
            "type": "node--timed_task",
            "id": "760a6c82-b0f1-4b2a-93d4-140061e878a9"
          },
          {
            "type": "node--timed_task",
            "id": "6548f256-a518-491a-8bba-d469778a3722"
          }
        ],
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/field_timedtasks",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/field_timedtasks"
        }
      }
    },
    "links": {
      "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1"
    }
  },
  "links": {
    "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine"
  }
}

Using Postman, I helped validate the basic API structure that my React app will use. Next, I’m going to look into how we can hook this up into a React application.

Starting a Vimeo embed at a given timestamp using the JS API

Vimeo by far provides the best hosted video embed service out there by far, in my opinion. This post explains how to change the behavior of a Vimeo embed to skip to a given timestamp when the user plays the video. We’ll use data attributes to store the start time in the HTML.

Why would I want this?

In my situation, the college I work at runs a robust news site that often times includes videos. In that context, we format our videos to include an opening branding screen, then a brief intro paragraph to orient people to the video they are about to watch. This works great for the news site, but what if we come across a situation where we want to skip that intro and go right into the video?

Vimeo Player JavaScript API to the rescue

The Vimeo Player JS API allows developers to easily interact with their Vimeo embeds. The directions on including it are pretty straight forward, they even offer a CDN hosted version to include:

<iframe src="https://player.vimeo.com/video/208129791" width="500" height="281" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>

Next, we look for our Vimeo Player in our JavaScript. In my example, our Vimeo videos are put into our site as follows:


<div class="video" data-start-time="5">
  <iframe src="https://player.vimeo.com/video/204427616?color=498957&title=0&byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

So, to access this Vimeo embed, in my JavaScript file I do something like:

if ($('div.video').length) {
  
  // Loop through all the videos
  $('div.video').each(function( index, value ) {

    // Grab the Vimeo player already on the page
    var vPlayer = new Vimeo.Player($(this).find('iframe')[0]);

    // Set a flag to determine if the player has been paused yet
    var pause = false;

    vPlayer.on('play', function() {

      // If the player has not yet been paused, start it at
      // the specified time indicated in data-start-time
      if (!pause)
        vPlayer.setCurrentTime($(value).attr('data-start-time'));
      });

    vPlayer.on('pause', function() {
      // Set the pause flag to true. Setting this prevents
      // the player from starting the video at 
      // the 'data-start-time' instead of just 
      // continuing the video where it was paused.
      pause = true;
    });
  });
}

Limitations

There’s nothing to stop people from rewinding back to the beginning and watching the intro if they choose, so if you’re trying to use this method to cut some video, it won’t work.

This also assumes you have the means to add data attributes to your HTML to store your start times. If you don’t have access to your HTML, you might need to rethink your approach.


Worked perfectly for my needs, hope it helps!

Follow up on scalable, maintainable CSS

I wrote down some ramblings about this subject a while back after first reading about it through @mrmrs on Twitter. The idea of single function CSS classes was weird and seemed unnatural at first, but I was very intrigued. I considered this idea around the same time I was realizing that, at work, I basically keep creating variations of the same designs over and over. Thus bloating our CSS file and wasting time developing components that were pretty much previously completed. Then a lightbulb went off. 💡

We were in the middle of a project at work and I decided to try something. I’d create a bunch of “helper” classes that would perform single functions on an HTML element. For instance, .text-center would align the text in the center of the element. .container-800 would create a container that was 800px wide. I created about 20 or 30 of these classes and tried to come up with rules that I routinely use when creating BEM or SMACSS style components.

The result? Incredibly fast iteration! I could piece together these classes in HTML to make interfaces in our CMS so quickly that I couldn’t deny there is some real value in this.

Another result: people who work on my team can no go forth and build awesome interfaces without necessarily needing to know about SCSS, preprocessors, etc. It lowers the barrier for entry and helps groups work together faster.

It makes designer and developers speak the same language. “I think there should be a little more padding on this container” can now be remedied by the designer or the developer by modifying the padding class on the element. If the designer doesn’t know CSS or any of the tooling the developer uses, it is still possible for the designer to change the interface to their liking. This is so powerful.

If you’d like to check out a library that has created tons of these “helper” classes already, look into Tachyons. It’s a wonderful concept and library.

A local Oswego counseling practice gets a website

Heather Rice CounselingMy wife, who is currently in grad school studying mental health counseling, is logging practicum hours with a local practitioner. Their old site was in Wix and had terrible SEO value and wouldn’t properly display on mobile phones. To remedy this, I whipped up a WordPress theme using Bootstrap v3, and gave them a fresh new site to build their practice off of. The site features a blog, which will educate the community on topics relating to mental health. Heather Rice, who offers counseling services in Oswego, NY, now has a simple new website built on WordPress that can grow with her business.

 

SUNY Oswego’s 2014 Annual Report

2014 Annual Report

I’m super proud of what our team at work was able to accomplish in the midst of a Drupal build/migration, as well as our day-to-day tasks. The writing is top-notch and dives deep into the happenings of SUNY Oswego over the past year.

The primary designer of the project was Kelli Ariel, who had a large part in deciding how the site looked. I took the lead on the front-end development. Since I was so involved in the front-end code and design, that’s mostly what this post will be about.

What did this project take to build?

It was about a month of throwing around ideas, designing, coding and Slack conversations. Our process usually involved Kelli coming up with a quick mock-up of what certain parts of the site would look like, then she’d pass those mocks to our Slack channel, some brief discussion would ensue with our team and then I’d code it to work on the web. It worked pretty well for us and allowed us to get a lot done in a short period of time.

Design, development, and content were all intertwined throughout the project. This helped quite a bit. The primary writer, Julie Blissert was very flexible to work with. We were able to get a content structure formed in the early part of the process so design and development could go on, even though the content was still in the process of being written. For example, by structuring the content, I mean the blocks that you can hover over on the site were defined as having:

  • A headline
  • Optional subhead
  • ~325 characters of content on hover

hover-blocks

Without designers, developers, and writers working together to define things like that, a project can get unnecessarily messy.

Development tools:

  • Bower to manage libraries
  • Grunt to build, minify, and concat files to cut down on requests and file size
  • Git for version control

Libraries used:

  • jQuery
  • Waypoints
    • Used to lock in the sticky horizontal navigation
    • Used to fire an event when you hit the bottom of the page. This would then slide out the ‘next’ arrow near the footer.
  • CountUp.js
    • Used in the number panels to count up our statistics
  • Chart.js
    • Used on the Facts and Figures page to build the charts
  • Bootstrap’s grid system
  • Modernizr for feature detection

Fun features

Theme Color Cupola Card

Theme Color

With support for theme-color in the newest versions of Android, it allows web developers to customize their site’s appearance within the Android OS. For example, I was able to change Chrome’s title bar to be in line with our brand colors. I was also able to add our signature cupola icon to the ‘card’ when the user looks at all open apps.

It’s little things like this that are invisible to most users, but adds to the experience.

Sticky Header

On every page, whether you are on a mobile device or a desktop computer, the navigation will ‘stick’ to the top of the view port, allowing users easy navigation to other sections of the site. It seems particularly useful on mobile devices since it can be cumbersome to scroll all the way back to the top of a long page in order to reach the navigation again.

Scrolling Meter

Our web technical lead, Rick Buck pushed for the addition of the scrolling meter, which gives the user a subtle sense of orientation while reading the page. While scrolling down the page, a small green bar will appear under the navigation that shows how far down the page you are. When the bar reaches the end of the screen, you’re at the bottom. This can give users the ability to know where they are on the page with a quick glance.

scrollingmeter

 

 

Using automation tools to deal with rem support in older versions of IE

We all know the wonders and joys of working with older versions of IE, namely < IE9. While I see a lot of sites are kind of throwing their hands up in the air when it comes to legacy IE support, I think its necessary to at least make sure your content is accessible. It’s a little irresponsible to throw up a message saying “Sorry, your browser is old. Download Google Chrome”. At the very least, you could serve them a print style sheet so they could read the content. While IE8 is dying a slow death, it still has a good amount of users.

I’ve been using rems in conjunction with ems now to give myself a consistent spacing unit when coding a website. As with all handy CSS properties, IE8 doesn’t support it so we have to look elsewhere. You could either a) Write pixel fallbacks before each time you use a rem.

h1 {
  font-size: 48px; /* for < IE8 */
  font-size: 3rem; /* for newer browsers */
}

IE will ignore the rem part but will pick up on the

font-size: 48px;

If this works for you that’s fine, but I’d rather have a script automate this process. That’s where grunt and rem_to_px comes in handy. My current grunt task is set up like so:

rem_to_px: {
  options: {
    baseFontSize: 16,
    removeFontFace: true,
  },
  dist: {
    src: ['source/css/style.css'],
    dest: 'public/css/ie/'
  }
},

The task will create a stylesheet where I told it do in

dest

that is the same filename as what is declared in

src

. This stylesheet will only contain rem to pixel substitutions. Then, in my HTML it’s simple to drop in a conditional statement to check if the browser is less than IE8. If so, I’ll include the stylesheet created by the grunt plugin.

<!-- link to the normal style sheet -->
<link rel="stylesheet" href="public/css/style.css">

<!--[if lte IE 8]>
<link rel="stylesheet" href="public/css/ie/style.css">
<!--[endif]>

Thoughts on my 2014

2014 has come to an end and it’s time to look ahead to 2015. 2014 was my first full year in a professional environment. I wrote my first jQuery plugin. This was also the year I really sunk my teeth into CSS preprocessors, automation, and version control to supplement my front end development.

Working for a full year doing web development has been a great experience. I used to think the hardest thing in programming was what to name your variables, but it turns out that – for me – it’s communicating your ideas. Because of this this, I want to blog more in 2015. I think it’ll help me understand my thoughts a little more. The ability to communicate technical ideas clearly to a non-technical audience is a skill I need to sharpen.

I dove into jQuery a little more thanks to videos by Paul Irish. This knowledge allowed me to write my first jQuery plugin. It’s nothing super amazing or technical, but it was a neat exercise in learning something new. Looking at example code of jQuery plugins made me feel uncomfortable at first, but as I stuck with it and kept reading, it started to gel together enough for me to write something that works.

This is the year I started using LESS as my CSS preprocessor of choice. I keep reading a lot about Sass, but haven’t tried it out yet because LESS does everything I need. LESS solves many problems I was tired of dealing with while writing plain CSS – namely lack of variables and mixins. The idea of compiling a few .LESS files into 1 CSS file seemed to make more organizational sense to me, so I never looked back.

Another addition to my workflow in 2014 was embracing front end tooling. I used to have no issue importing 9 non-minified .js files in the footer of a site. I used to FTP all my files manually every time they were updated. I used to download external libraries by just going to the library’s website, downloading the zip, then extracting it to my project. Now, I use grunt tasks to concatenate and minify my scripts and CSS. I use rsync to transfer my project to staging or production servers. I use bower to manage my site’s dependencies, allowing me to quickly download and update external libraries. These things used to seem advanced and scary to learn, but since I’ve gotten to know these tools, I can’t imagine working without them.

Finally, 2014 was the year I started using version control. Git tutorials and YouTube videos really helped me understand how important it is to a developer’s workflow, how it works, and how to use this powerful tool to manage your projects. Gone is the fear that something irreversible will happen to my project. I can rest assured knowing each version is backed up on my local repo and in the hosted repo (I like using BitBucket).

TL;DR – My workflow changed a lot for the better in 2014, and I got the chance to learn more about jQuery plugins (and as a side effect I now understand JavaScript and jQuery better). Looking forward to 2015, I hope to learn as much or more.

To Bootstrap, or not to Bootstrap?

Front end frameworks are great time savers for getting your project up and running quickly, but is the time you save setting up the project wasted when it comes to customizing the design?

At a conference earlier this year, I got the opportunity to meet Brad Frost, which was super awesome. I told him at the time I used Bootstrap in my front end development and he said something that got me thinking. He said that in his experience, front end frameworks (like Bootstrap, Foundation, etc) get you about 80% of the way, then you spend the last 20% fighting them while you customize it to not look like Bootstrap.

I could certainly relate to this. Where I work, there’s usually a designer on projects who drives what the website will look like. Very very often, the designer would come up with a very doable design, but it couldn’t be replicated in Bootstrap without heavy customization. I could get a quick prototype up, but it would take a considerable amount of customization before it was production ready in the eyes of the designer. By the time we were done, very little of Bootstrap’s styles were even visible, but they were still there in the code.

I saw this and it made me think about what Brad said. I was essentially using Bootstrap as a base, then customizing it so much that you couldn’t really tell it was Bootstrap. I might as well be writing it my way from the start, instead of hacking Bootstrap’s styles. So instead of hacking Bootstrap’s styles, I started compiling my own stylesheet only using the Bootstrap LESS files I need. Now, there is less of me fighting with Bootstrap and overriding styles. I only use their utility classes, vendor prefixes, grid, and scaffolding LESS files now and it gives me the quick start I want when doing a project, but allows for massive customizations without fighting against Bootstrap’s styles.

This is currently working pretty nicely for me. I could see myself in the future having my own grid system, mixins, etc that I take with my from project to project, but right now the Bootstrap ones are working great.