Cloud and Client

Are they friends?


Modern Applications

  • Self Contained
  • MV* Frameworks
    • Logic moving off the server
  • Offline First
  • Device Aware

modern app

Cloud? Server?

Who needs 'em?

  • Consolidate the concept
    of permanent application.
  • Tools:
    • AppCache
    • localStorage
    • File API
    • IndexedDB
cloud and server are the friends of html5

Cloud? Server?

Who needs 'em?

  • Minimize interactions with the "Cloud"
    • Offline first
      • Flaky connections
      • Airport...deserted island...a cloud...
    • Pretend that there's no internet connection
    • Abstract sync layer from UI:
navigator.onLine & window.(ononline|onoffline)

Modern Applications

How do they rely on the cloud?

  • Offload computation
    • Datacenter in my hand
  • Persistence
  • Same User
    • Multiple Clients
  • Sharing
cloud and server are the friends of html5

Minimize interactions he says

Offline first he says

Server matters? Minimize interactions!

Lick Bowl Clean

Google Cloud Platform

Tools to help our friends (the mobile client)

Modern Apps and The Server Conundrum

  • Who wants to run a server?
    • Running a server is hard
    • Let's go shopping

it's hard to lift servers to the air

Modern Apps and The Server Conundrum

  • But who wants to run a server?
    • Traffic Spikes
    • Client Server communication
    • Serialization
    • OAuth Dance

Google Cloud Platform

The Goal

Google App Engine

Google App Engine
Java Logo Go Logo Python Logo PHP Logo

Google Cloud Endpoints

Endpoints Diagram

Google APIs

The Discovery Document

Discovery Document

So what?

Client Libraries Like Whoa!!

  • Web
    • JavaScript: google-api-javascript-client
    • Node.js: google-api-nodejs-client
    • Dart: google-api-dart-client
  • Mobile
  • Server-side
    • Ruby: google-api-ruby-client
    • Python: google-api-python-client
    • Go: google-api-go-client
    • PHP: google-api-php-client
    • ...

Google Cloud Endpoints

Your own API, your own Discovery Document

Using endpoints-proto-datastore

Data and Interface One and the Same

class BankAccount(EndpointsModel):
  owner = ndb.StringProperty()
  balance = ndb.FloatProperty()
POST /_ah/api/bank/v1/account/update

  'owner': 'Danny Hermes',
  'balance': 13.37

Using endpoints-proto-datastore

Data and Interface One and the Same

  • Who cares about APIs?
  • A ... P ... I ...?

Let's talk Offline

(Client baby!)



No connection? No problem

  • A mobile web app requires a "one-time" connection
    After this, everything should work
  • First save locally
  • Sync - Sync - Sync in the background

HTML5 And the Offline World

Photo Metadata

Offline World

  • Offline storage in mobile web is complex
  • Lawnchair abstracts a common interface for interacting
    • localStorage / Web Storage
    • IndexedDB / WebSQL
  • Save in offline storage, without worrying about the environment

Let's talk Online

(Calling APIs)

OAuth 2.0? You got it


  <button class="g-signin"
      data-scope=" ...">

Let's see those benefits of Discovery


Google's APIs:

// Load the API
gapi.client.load('urlshortener', 'v1', loadingSuccessCallback);

// Call the API
var payload = {'shortUrl': ''};

Your Own API:

var apiRoot = window.location.origin + '/_ah/api';
gapi.client.load('picturesque', 'v1', loadingSuccessCallback, apiRoot);

var payload = {'key': '1337'};;

What happened to Minimize interactions?

  • gapi.client.load uses a
    network call every time
  • Can't waste one request in
    flaky conditions
  • To get the most, we must
    tweak slightly

Lick Bowl Clean

Let's Get Our Hands Dirty

(Just a little bit)

Never fear!

We can still use gapi

var payload = {'key': '1337'};
var rpcRequestObject = gapi.client.rpcRequest(
    '', 'v1', payload);
// Set the root to the custom API root

Separation of Concerns

UI Layer and Data Layer

Client: Ready for Action

// Configure store
var uiCallbacks = { ... };
var store = new;

// Add photo both locally and on the server
  store.addPhoto(title, base64Photo, mimeType, description);

Cloud: Ready for Action

class Photo(EndpointsModel):
  title = ndb.StringProperty()
@endpoints.api(name='picturesque', version='v1', ...)
class PicturesqueApi(remote.Service):

  @Photo.method(user_required=True, path='photo', name='photo.create')
  def PhotoCreate(self, photo):
    # do some validation
    return photo


What does it actually do?

  • Separate concerns
  • UI and Data not bound together
  • Use navigator.onLine and window.ononline
    • Communicate with the API directly OR
    • Add task to a sync queue


What does it actually do?

var task = new
    PicturesqueApp.api.callPicturesqueAPI, 'photo', 'create',
    apiPayload, anonymousCreateCallback); = function() {
  // Order is important here.
  if (! {;
  } else if (navigator.onLine) {;
  } else {

Utilizing Expensive Computation

Queries FTW

Did we mention?

Minimize Interactions

Minimize Interactions

Rinse and Repeat

  • Load photos from Cloud sequentially
  • Display and Store Locally
  • Track the updated time stamp
  • More than one page of results? You know what to do
    • Communicate with the API directly OR
    • Add task to a sync queue

Client Side Sample Code = function() {
  var currentDataStore = this;
  var getRemoteCallback = function() {
        currentDataStore.getPhotosCompletionCallback, lastUpdated,
        pageToken, limit);

Intelligent Queries

(Offload computation, anyone?)

  • Let persistence layer work harder
  • Create query indexes based on properties needed
  • index.yaml

- kind: Photo
  - name: owner
  - name: updated

Intelligent Queries

(Offload computation, anyone?)

from endpoints_proto_datastore.ndb import EndpointsAliasProperty

class Photo(EndpointsModel):


  @EndpointsAliasProperty(name='lastUpdated', setter=LastUpdatedSet)
  def last_updated(self):

Intelligent Queries

(Offload computation, anyone?)

  def LastUpdatedSet(self, value):
      last_updated = utils.DatetimeValueFromString(value)
      if not isinstance(last_updated, datetime.datetime):
        raise TypeError('Not a datetime stamp.')
    except TypeError:
      raise endpoints.BadRequestException(
          'Invalid timestamp for lastUpdated.')

    self._endpoints_query_info._filters.add(Photo.updated >= last_updated)

Intelligent Queries

(Offload computation, anyone?)

  @Photo.query_method(query_fields=('limit', 'pageToken', 'title',
                      path='photos', name='photo.list')
  def PhotoList(self, query):
    return query.order(Photo.updated)


Things we would like to have talked about and/or built

Ways to Extend

Deep Breath

Key Take Aways

  • Build powerful applications with Google Cloud Endpoints
  • Minimize and optimize client and cloud communication
  • Use libraries and abstractions to minimize work
  • Leverage Modern Browser Features

Check out related Sessions

Cloud Platform and Chrome

Thank You!


<Thank You!>