Getting Your Content Where You Need It

— with —



Chris Hamper
Labs Engineer

What is JSON API?

  • Open standard:
  • Helps create RESTful APIs quickly and consistently
  • Describes a consistent format for exchanging interrelated data using JSON
  • Anti-bikeshedding tool

What Does JSON API Enable?

Content/data interchange between disparate systems

  • Communicate across CMSs, Frameworks, Languages, etc.
  • Decoupled frontend architectures
  • Multi-channel content

Why Not Just Use Core REST?

Configuration is a pain:


langcode: en
status: true
    - basic_auth
    - node
    - serialization
id: entity.node
plugin_id: 'entity:node'
granularity: method
      - json
      - basic_auth

Collections of entities are a pain:

  • Need to create REST export Views
  • Need separate export for each entity type, sort, etc.

Handling interrelated data is a pain:

To get all information for an Article node:

  1. GET /node/1
  2. GET /user/123
  3. GET /taxonomy/term/40
  4. GET /taxonomy/term/41
  5. GET /taxonomy/term/42

That Doesn't Sound Like Much Fun

How About JSON API?

JSON API On Drupal

  • Enabled simply by installing a contrib module
  • Uses Drupal's Users & Permissions
  • Gives you all the power of the JSON API standard

JSON API Endpoints

Follow a very consistent, logical pattern:
  • /article
    Get the collection of all "article" resources
  • /article/1
    Get article with ID of "1"
  • /article/1/author
    Get "author" resource related to article 1

Example Request:

GET /article/1 HTTP/1.1
Accept: application/vnd.api+json


  "data": {
    "type": "article",
    "id": "1",

    "attributes": {
      "title": "Example JSON API Response",
      "body": "Lorem ipsum dolor sit amet..."

    "relationships": {
      "author": {
      	"data": { "type": "person", "id": "9" }

JSON API CRUD Operations

Follow RESTful API conventions:

  •   POST → Create new resource
  •    GET → Retrieve resource(s)
  •  PATCH → Update resource
  • DELETE → Delete resource

Only GET can involve multiple resources

Creating a New Resource

POST /article HTTP/1.1
Accept: application/vnd.api+json

  "data": {
    "type": "article",

    "attributes": {
      "title": "New Article"
      "body": "Wow! JSON API rocks!"

    "relationships": {
      "author": {
      	"data": { "type": "person", "id": "42" }

Updating an Existing Resource

PATCH /article/1 HTTP/1.1
Accept: application/vnd.api+json

  "data": {
    "type": "article",
    "id": "1",

    "attributes": {
      "title": "The New Title!"

Advanced Resource Fetching

Caveat: implementation details are left up to the server

Sorting Resources


GET /article?sort[sort-title][path]=title HTTP/1.1
Accept: application/vnd.api+json

GET /article?sort[sort-title][path]=title&
             sort[sort-title][direction]=DESC HTTP/1.1
Accept: application/vnd.api+json

Filtering Resources


GET /article?filter[uid][value]=42 HTTP/1.1
Accept: application/vnd.api+json

GET /article?filter[by-created][condition][path]=created



GET /article?page[offset]=20

The Very Best Thing About JSON API?

It's a standard!

Drupal has the "JSON API" module

Ember.js is built to use JSON API by default

Wordpress has plugins

Libraries for Java and Android, iOS

What can we do with it?


  • Drupal 8 backend with JSON API contrib module
  • Ember.js frontend application

Drupal Setup

1. Install JSONAPI Module

              composer require "drupal/jsonapi:^1.0"
              drush en -y jsonapi

2. CORS Configuration

# Add to sites/default/services.yml
    enabled: true
    # Specify allowed headers, like 'x-allowed-header'.
    allowedHeaders: ['Content-Type', 'Access-Control-Allow-Headers', 'Authorization', 'X-Requested-With', 'X-CSRF-Token']
    # Specify allowed request methods, specify ['*'] to allow all possible ones.
    allowedMethods: ['POST', 'GET', 'OPTIONS', 'PATCH', 'DELETE']
    # Configure requests allowed from specific origins. Ideally this should not be '*', but rather a list of specific hosts.
    allowedOrigins: ['*']
    # Sets the Access-Control-Expose-Headers header.
    exposedHeaders: true
    # Sets the Access-Control-Max-Age header.
    maxAge: false
    # Sets the Access-Control-Allow-Credentials header.
    supportsCredentials: true

Ember.js App Setup

Create Ember app and dev stack:

ember init

Install ember-data-drupal addon:

ember install ember-data-drupal

Create an ember-data-drupal adapter:

// app/adapters/application.js

import DrupalJSONAPIAdapter from 'ember-data-drupal/adapter';

export default DrupalJSONAPIAdapter.extend({
  host: '',
  namespace: 'jsonapi'

Create an ember-data-drupal serializer:

// app/serializers/application.js

import DrupalJSONAPISerializer from 'ember-data-drupal/serializer';
export default DrupalJSONAPISerializer.extend();

Configure resource types to use:

// config/environment.js

var ENV = {
  drupalEntityModels: {
    "article": {},

Set up models, routes, and templates:

// app/models/article.js
import DS from 'ember-data';
export default DS.Model.extend({
  uuid: DS.attr(),
  title: DS.attr(),
  body: DS.attr()

// app/routes/article.js
import Ember from 'ember';

export default Ember.Route.extend({
  model(params) {
    return this.get('store').findRecord('article', params.uuid);

Demo Time!

Useful Links

Thank You!

Slides and example code available on GitHub


Get in Touch: