Getting Your Content Where You Need It

— with —

JSON API

 

Chris Hamper
Labs Engineer

What is JSON API?

  • Open standard:  http://jsonapi.org
  • 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
dependencies:
  module:
    - basic_auth
    - node
    - serialization
id: entity.node
plugin_id: 'entity:node'
granularity: method
configuration:
  GET:
    supported_formats:
      - json
    supported_auth:
      - 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
					

Response:


{
  "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
            &filter[by-created][condition][value]=2015-10-10
            &filter[by-created][condition][operator]=<
        

Pagination

 


GET /article?page[offset]=20
            &page[limit]=10
        

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?


Demonstration:

  • 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
cors.config:
    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: 'http://www.yourbackendsite.com',
  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
https://chris-hamper.github.io/getting-your-content-json-api/ https://github.com/chris-hamper/drupal-ember-jsonapi-demo

 

Get in Touch:

drupal.org/u/hampercm

@hampercm

chris.hamper@acquia.com