Designing api for mobile apps

Maciek Opała
Wojtek Erbetowski

agenda

  • Motivation
  • A little bit about REST
  • Welcome to world of limits
  • Design patterns

motivation

  • Take a look from a different perspective
  • Provide a better user experience

about us

teammates @ polidea

experience

  • Flow - Gradle based mobile build system
  • RoboSpock - Android Unit testing framework
  • Rooms booking system
  • Company contacts apps
  • Company approval process
  • uTest - in-the-wild testing

A word about rest

  • Definitely a buzzword
  • Service design pattern, close to HTTP 1.1
  • Guidelines for services layout

back to the roots


REST
Table 5-1: REST Data Elements
Data Element Modern Web Examples
resource the intended conceptual target of a hypertext reference
resource identifier URL, URN
representation HTML document, JPEG image
representation metadata media type, last-modified time
resource metadata source link, alternates, vary
control data if-modified-since, cache-control

the "re" in rest

stands for representational

updating resources


  • Creation: POST
  • Modifications: PUT, PATCH (RFC5789)

why isn't it perfect for mobile?

Meet new limitations

throughput and latency

Connection speed market share

updating app takes long


Average submission time:

  • Android - few minutes
  • iOS - few days
  • Windows Phone - few days


How often user updates app?

language and tools are very limited

Exercise: What does it take to call simultaneously several URLs and join results?

JAVA

  • Create an Executor instance
  • Implement Runnable interface for every task
  • Execute all tasks (synchronization?)
  • Render view
  • Exception handling? Tiresome..

Groovy

def result = ""

def threads = (1..3).collect {
  Thread.start {result += new URL("https://google.com").text}
}

threads*.join()
println result

Lower processing power

Patterns (lessons learnt)

what types of apps do we discuss?


Homegrown!
Public mobile APIs(?)

http methods

How do you update a small information?
POST with the whole object
PATCH coming right up

using actions in paths


POST /account/3145/withdraw



thinner client, more control

  • analytics
  • bug fixing
  • optimizations

hateoas

... and linking between resources
            {
"users" : [
  {
    "id": 1234,
    "name": "Bob",
    "roles": [
      {"href": "http://api.example.com/roles/1"},
      {"href": "http://api.example.com/roles/2"},
      {"href": "http://api.example.com/roles/3"}
    ],
  }
]

}
        

embedded relates

{
"users" : [
  {
    "id": 1234,
    "name": "Bob",
    "roles": [
      {"name": "ADMIN"},
      {"name": "OPERATOR"},
      {"name": "USER"}
    ],
  }
]

}

multiple calls from mobile

same service? different services?

Paging

  • Should be big enough not to require to many connections
  • Should let the client minimize the risk of loosing data

http cache

  • iOS (AfNetworking + NSURLCache)
  • Android (Retrofit + OkHttp + HttpResponseCache)

minimizing data size

example

{
    "appKey": null,
    "applicationFeatures": [
        "TM_HTML_COMPLETE",
        "ACCESS_SPUTNIK_IM_APPLICATION",
        "APPHANCE_INTEGRATION",
        "TM_TOP_TESTERS",
        "VOLTRON_DASHBOARD"
    ],
    "authDate": 1396788850862,
    "chatUUID": "1234567890",
    "code": "4c4712a4141d261ec0ca8f90379506854c4712a4141d261ec0ca8f90379506854c4712a4141d261ec0ca8f90379506854c4712a4141d261ec0ca8f9037950685",
    "grantedAuthorities": [
        "PERMISSION_BUG_PAYOUT_VIEW",
        "PERMISSION_PROJECT_VIEW",
        "PERMISSION_TESTER_PAYMENTS_VIEW",
        "PERMISSION_USER_ACCOUNT_VIEW",
        "PERMISSION_REVIEW_PAYOUT_VIEW",
        "PERMISSION_TEST_CYCLE_VIEW",
        "PERMISSION_USER_ACCOUNT_CLOSE",
        "PERMISSION_USER_ACCOUNT_EDIT",
        "PERMISSION_BUGS_AND_TEST_CASES_VIEW",
        "PERMISSION_BUG_EDIT",
        "PERMISSION_TEST_CYCLE_REVIEW_READ",
        "PERMISSION_PRODUCT_VIEW",
        "PERMISSION_BUG_CLIPBOARD_COPY",
        "PERMISSION_REQUESTED_INFO_SEND",
        "PERMISSION_TEST_RUN_TEST_CASE_SELF_ASSIGN",
        "PERMISSION_BUG_DISCARD",
        "PERMISSION_BUG_DISPUTE",
        "PERMISSION_USABILITY_SURVEY_RESPONSE_VIEW",
        "PERMISSION_USABILITY_SURVEY_RESPONSE_DISCARD",
        "PERMISSION_USABILITY_SURVEY_RESPONSE_EDIT",
        "PERMISSION_USABILITY_SURVEY_RESPONSE_CREATE",
        "PERMISSION_TEST_CYCLE_COVERAGE_VIEW",
        "PERMISSION_TEST_RUN_ASSIGNMENT_EXECUTE",
        "PERMISSION_TEST_CYCLE_PRODUCT_DESCRIPTION_VIEW",
        "PERMISSION_TEST_CYCLE_OBJECTIVES_VIEW",
        "PERMISSION_BUG_CREATE",
        "PERMISSION_TEST_CASE_TAB_VIEW",
        "PERMISSION_TEST_CYCLE_REVIEW_CREATE",
        "PERMISSION_TEST_RUN_RESULT_ATTACHMENT_UPLOAD",
        "PERMISSION_USABILITY_SURVEY_RESPONSE_PAYOUT_EDIT",
        "PERMISSION_BUG_FIX_WORTHINESS_PRICE_VIEW",
        "PERMISSION_TEST_CYCLE_REVIEW_EDIT",
        "PERMISSION_USABILITY_SURVEY_RESPONSE_PAYOUT_VIEW",
        "PERMISSION_TEST_CYCLE_REVIEW_DISCARD",
        "PERMISSION_TEST_CASE_PAYOUT_VIEW",
        "PERMISSION_TEST_CYCLE_REVIEW_MESSAGE_CREATE",
        "DATA_GRID_TESTER_VIEW",
        "PERMISSION_USABILITY_SURVEY_PAYOUT_VIEW"
    ],
    "impsersonatedUserId": null,
    "ipAddress": "127.0.0.1",
    "roles": [
        3
    ],
    "token": "43v4j3gv4jh3n23z896b2c32xbfekszb3",
    "userId": 1234,
    "userName": "frank@sinatra.com"
}

Data format


XML JSON
5234 bytes 1934 bytes (37%)

fields subsets


JSON JSON subset
1934 bytes 88 bytes (4.5%)

compression


JSON subset JSON subset + GZIP
88 bytes 101 bytes (115%)


Overall: 1,9% of initial size!

versioning


3 schools of versioning

path parameter


http://api.example.com/v1

media type (or custom header)


application/vnd.example.com.v1+json

subdomain


http://v1.api.example.com

documenting services

and swagger

case study - utest app

joining responses


Reduced:

  • 36 to 20 endpoints
  • 86 to 20 calls (visiting all screens once)
  • parallel requests

video

middleware

q&a