Backend-less Development Revisited

Tomasz Ducin

Tomasz Ducin

10th December 2016, Wrocław

Backend-less Development Revisited

Tomasz Ducin

11th October 2016, Kraków

Backend-less Development Revisited

Tomasz Ducin

15th September 2016, Kraków

Backend-less Development Revisited

Tomasz Ducin

15th July 2016, London

Backend-less Development Revisited

FullStack

FullStack

Backend-less Development Revisited

conference on JavaScript, Node & Internet of Things

conference on JavaScript, Node & Internet of Things

Tomasz Ducin

15th July 2016, London

Tomasz Ducin

16th June 2016, Darmstadt

Backend-less Development Revisited

Tomasz Ducin

13th May 2016, Kraków

Backend-less development revisited

geecon

Tomasz Ducin

11th April 2016, Warsaw

Backend-less development revisited

Backend-less development revisited

Tomasz Ducin

25th February 2016, the WEB

Backend-less development revisited

Backend-less development
in AngularJS

Tomasz Ducin

22nd February 2016, Warsaw

#6

Backend-less development
in AngularJS

Tomasz Ducin

JavaScript, Java, Python

senior software consultant @ Cybercom Poland

trainer @ Bottega IT Solutions

blah, blah, blah...

Agenda

  1. single page apps
  2. what is BLD, why & what for?
  3. how does it work?
  4. BLD in AngularJS & Sinon.JS
  5. automation

traditional

server-side web apps

AJAX

Single-Page Application

SPA

REST API

client-side web apps

Single Page Apps

flawless solution

solution architect

tight coupling

FE

BE

What is more important?

let's modify JSON response!

but why?

because the new structure fits better

so what is...

?

backend-less development

server-less architecture

server-less architecture

~90%

actual development

FE-BE
sync

~10%

:

Frontend development
with Backend-less approach

Technical Perspective

  • BE changes force FE changes

  • BE needs to be implemented prior to FE

  • backends being unavailable

interface testing

depends on the API

  • private customer banking interface
  • 1+ year to go on production
  • deployed on 120+ Scandinavian banks

BLD experience: Retail Banking

why would I need BLD

if I've got backend anyway

Business Perspective

  • budget-driven development

  • big, distributed teams

  • backends being unavailable

mock your APIs!

MOCKS

MOCKS

MOCKS

DATABASE

BLD approach

  • delivering data: different levels

  • mocking backend behavior
    • different for GET, POST, PUT, DELETE
    • delayed responses (timeout)
    • request parameters, random responses
  • generating data:
    • JSON Schema
    • Contract-first Design

JSON Schema

{
  "title": "Example Schema",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "age": {
      "description": "Age in years",
      "type": "integer",
      "minimum": 0
    }
  },
  "additionalProperties": false,
  "required": ["firstName", "lastName"]
}
{
  "firstName": "John",
  "lastName": "Lennon",
  "age": 40
}
{
  "firstName": "Paul",
  "lastName": "McCartney"
}

node.js-based

fake data generator

stub server

API Blueprint

Swagger

RAML:
RESTful API Modelling Language

Contract-first Design

  • stored in repository
  • available in CI
  • used by many
    automates
  • URLs
  • HTTP methods
  • HTTP status codes
  • HTTP content-types
  • HTTP headers
  • content itself
/songs
  get
  post
  /{songId}
    get
    /file-content
      get
      post
/artists
  get
  post
    /{artistId}
      get
      /albums
        get
/albums
  get
  post
    /{albumId}
      get
      /songs
        get
/songs:
  description: Collection of available songs in Jukebox
  get:
    description: Get a list of songs based on the song title.
    queryParameters:
      songTitle:
        description: "The title of the song to search (...)"
        required: true
        minLength: 3
        type: string
        example: "Get L"
[...]

API Contracting

Example

[...]
    responses:
      200:
        body:
          application/json:
            example: |
              "songs": [
                  {
                    "songId": "550e8400-e29b-41d4-a716-446655440000",
                    "songTitle": "Get Lucky"
                  },
                  {
                    "songId": "550e8400-e29b-41d4-a716-446655440111",
                    "songTitle": "Loose yourself to dance"
                  },
                  {
                    "songId": "550e8400-e29b-41d4-a716-446655440222",
                    "songTitle": "Gio sorgio by Moroder"
                  }
                  ]

Sinon.JS code & demo

XMLHttpRequest

var fs = sinon.fakeServer.create();

fs.respondWith(HTTP_METHOD, URL,
  [HTTP_STATUS, HEADERS, DATA]
);

sinon.fakeServer

Fake HTTP backend implementation

var value = [{"name": "John", "age": 68 }, ...];

var fakeServerWrapper = {
    init: function() {
        this.fs = sinon.fakeServer.create();

        this.fs.respondWith("GET", 'customers/',
            [200, { "Content-Type": "application/json" },
                JSON.stringify(value) ]);

        this.fs.autoRespond = true;
    },

    restore: function() {
        this.fs.restore();
    }
};

// fakeServerWrapper.init();

AngularJS code & demo

when(method, url, [data], [headers])
  .respond(HTTP_STATUS, DATA, HEADERS);

whenGET(url, [headers]);
whenHEAD(url, [headers]);
whenDELETE(url, [headers]);
whenPOST(url, [data], [headers]);
whenPUT(url, [data], [headers]);
...

$httpBackend

Fake HTTP backend implementation

when(method, url, [data], [headers])
  .respond(function(...){
    return [HTTP_STATUS, DATA, HEADERS];
  });

whenGET(url, [headers]);
whenHEAD(url, [headers]);
whenDELETE(url, [headers]);
whenPOST(url, [data], [headers]);
whenPUT(url, [data], [headers]);
...
var demoApp = angular.module('demoApp', ['ngResource']);

demoApp.controller('fetchCtrl', function ($scope, $resource) {
    var Events = $resource('/events/:eventId', {
        eventId: '@id'
    });

    $scope.fetch = function () {
        Events.query({'eventId': 6}, function (data) {
            // do something with data
        });
    };
});
// ...
// mock part

var data = [{
  "name": "FullStack",
  "edition": 2016
}, ...];

demoApp.config(function ($provide) {
    $provide.decorator('$httpBackend',
        angular.mock.e2e.$httpBackendDecorator);
});

demoApp.run(function ($httpBackend) {
    $httpBackend.whenGET('/events/6').respond(
        200, data, {header: 'one'}
    );
});

mock automation

...
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="mock.js"></script>
...

mock.js bundle

mock.js bundle

1. mock data

2. mock logic

[{
  "firstName": "Hans",
  "lastName": "Schmidt",
  "age": 47,
  "city": "Berlin"
}, {
  "firstName": "Karl",
  "lastName": "Fischer",
  "age": 26,
  "city": "Hamburg"
}, {
  "firstName": "Peter",
  "lastName": "Mueller",
  "age": 71,
  "city": "Leipzig"
}, ...]

3. dependencies

var mockData = require('data.json');

function applyGETcollection(){ ...
  return mockData;
}

function applyGETitem(id){ ...
  return mockData[id];
}

function applyPOST(item){ ...
  mockData[item.id] = item;
}

function applyPUT(item){ ...
  mockData.push(item);
}

function applyDELETE(id){ ...
  delete mockData[id];
}
  • $httpBackend for AngularJS

  • fakeServer for sinon.js

  • etc.

            mock.js
        

Abstract
Syntax
Tree

var modB = require('b');
var modC = require('c');
module.exports = ...
module.exports = ...

a.js

var modD = require('d');
module.exports = ...

b.js

c.js

module.exports = ...

d.js

a.js
b.js
c.js
d.js

bundle.js

a.js
b.js
c.js
d.js

bundle.js

native require
node.js function

bundle working
in the browser

a.js
b.js
c.js
d.js

loose files

...
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="mock.js"></script>
...

mock data

mock logic

dependencies

mock.js 
  • e2e tests (integration)

  • production

grunt serve [--mock], gulp serve [--mock]
  • development

  • e2e tests (UI only)

mocks

no mocks

THX!

independent clients make

everybody benefit