API Love Story

{"api": "❤"}

Who?

Ahmad Nassri

{
  "name": "Ahmad Nassri",
  "url": "http://www.ahmadnassri.com/",
  "twitter": "http://twitter.com/ahmadnassri",
  "email": "ahmad@ahmadnassri.com",
  "bio": "Technologist, Entrepreneur, Dog Lover.",
  "work": {
    "company": "Mashape.com",
    "job": "API Master"
  },
  "activites": [
    "blogging",
    "open-source projects"
  ],
  "meetups": [
    "#YeomanTO",
    "#FBTO"
  ]
}

Mashape is the largest API hub in the world where you can consume, distribute, monitor and monetize your public and private APIs.

  • A revenue generating platform for technology startups
  • Powering thousands of APIs and Applications
  • Used in almost 100 countries by thousands of developers
  • Adopted in every major industry including finance, healthcare, military, agriculture, insurance, government, media, e-commerce, retail, aviation, manufacturing and telecom.
In Egyptian mythology, Apis, is a bull-deity that was worshipped in the Memphis region. Apis served as an intermediary between humans and an all-powerful god.

Egyptology

an application programming interface (API) specifies a software component in terms of its operations, their inputs and outputs and underlining types. Its main purpose is to define a set of functionalities that are independent of their respective implementation, allowing both definition and implementation to vary without compromising each other.

Computer Programming

Web Services

A Web service is a method of communication between two electronic devices over a network. It is a software function provided at a network address over the web with the service always on as in the concept of utility computing.

© http://www.slideshare.net/markedgington

RESTful Web APIs

Originally communicated by Roy Fielding in his doctoral dissertation The REST architectural style describes six constraints:

  • Uniform Interface
  • Stateless
  • Cacheable
  • Client-Server
  • Layered System
  • Code on Demand (optional)

REpresentational State Transfer

API Love Story

{"api": "❤"}

RESTful Web API Love Story

{"restful-web-api": "❤"}

Story so far...

  • [1991] Started as notes written by Tim Berners Lee.
  • [1996] First standard HTTP/1.0 version (RFC 1945).
  • [1999] HTTP/1.1 (RFC 2616) & REST
  • [2007] Refactoring with HTTP/1.1 bis
  • [2009] SPDY
  • [2012] IETF working group for HTTPbis released draft of HTTP 2.0
    • SPDY was chosen as the starting point.
  • [June 2014] RFC2616 is Dead!
  • [Aug 2014] HTTP 2.0 Last Call!
  • Core Specifications
    • RFC7230 - HTTP/1.1: Message Syntax and Routing
    • RFC7231 - HTTP/1.1: Semantics and Content
  • Optional / Widely Used
    • RFC7232 - HTTP/1.1: Conditional Requests
    • RFC7233 - HTTP/1.1: Range Requests
    • RFC7234 - HTTP/1.1: Caching
    • RFC7235 - HTTP/1.1: Authentication
    • RFC6265 - HTTP State Management Mechanism

These RFCs collectively obsolete all preceding RFCs defining HTTP, including RFC1945, RFC2068 and RFC2616.

http://httpwg.github.io
http://http2.github.io

How to GET a Cup of Coffee

http://www.infoq.com/articles/webber-rest-workflow

The Customer Workflow

 The Barista's Workflow

Workflow


http POST localhost:3000/order drink=coffee
HTTP/1.1 201 Created
Connection: keep-alive
Content-Length: 132
Content-Type: application/json; charset=utf-8
Date: Tue, 26 Aug 2014 16:37:55 GMT
Location: http://localhost:3000/order/1409071075510

{
    "cost": 3, 
    "drink": "coffee", 
    "next": {
        "rel": "http://localhost:3000/payment", 
        "uri": "http://localhost:3000/payment/order/1409071075510"
    }
}
POST /order HTTP/1.1
Host: localhost:3000
Content-Type: application/json; charset=utf-8

{
    "drink": "coffee"
}
router.post('/order', function(req, res) {
  // generate new id
  var id = '123';

  // create object
  var order = {
    drink: req.body.drink,
    cost: 3,
    next: {
      rel: 'http://' + req.get('host') + '/payment',
      uri: 'http://' + req.get('host') + '/payment/order/' + id
    }
  };

  // save order
  store.set(id, order);

  // status
  res.status(201);

  // headers
  res.set({'Location': 'http://' + req.get('host') + '/order/' + id});

  // respond
  res.send(order);
});

http OPTIONS localhost:3000/order/1409071075510
HTTP/1.1 200 OK
Allow: GET,PUT
Connection: keep-alive
Content-Length: 7
Content-Type: text/html; charset=utf-8
Date: Tue, 26 Aug 2014 16:54:44 GMT

GET,PUT
OPTIONS /order/1409071075510 HTTP/1.1
Host: localhost:3000

http PUT localhost:3000/order/1409071075510 additions=shot
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 151
Content-Type: application/json; charset=utf-8
Date: Tue, 26 Aug 2014 16:53:07 GMT
Location: http://localhost:3000/order/1409071075510

{
    "additions": "shot", 
    "cost": 4, 
    "drink": "coffee", 
    "next": {
        "rel": "http://localhost:3000/payment", 
        "uri": "http://localhost:3000/payment/order/1409071075510"
    }
}
PUT /order/1409071075510 HTTP/1.1
Host: localhost:3000
Content-Type: application/json; charset=utf-8

{
    "additions": "shot"
}
router.put('/order/:id', function(req, res) {
  var id = req.param('id');
  var order = store.get(id);

  // modify order
  order.additions = req.body.additions;
  order.cost += 1;

  // save order
  store.set(id, order);

  // status
  res.status(200);

  // headers
  res.set({'Location': 'http://' + req.get('host') + '/order/' + id});

  res.send(order);
});

http PUT localhost:3000/order/1409071075510 additions=moar+shots
HTTP/1.1 409 Conflict
Connection: keep-alive
Content-Length: 151
Content-Type: application/json; charset=utf-8
Date: Tue, 26 Aug 2014 16:53:07 GMT
Location: http://localhost:3000/order/1409071075510

{
    "additions": "shot", 
    "cost": 4, 
    "drink": "coffee", 
    "next": {
        "rel": "http://localhost:3000/payment", 
        "uri": "http://localhost:3000/payment/order/1409071075510"
    }
}
PUT /order/1409071075510 HTTP/1.1
Host: localhost:3000
Content-Type: application/json; charset=utf-8

{
    "additions": "moar shots"
}

http GET localhost:3000/order/1409071075510
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 151
Content-Type: application/json; charset=utf-8
Date: Tue, 26 Aug 2014 16:53:07 GMT
Location: http://localhost:3000/order/1409071075510

{
    "additions": "shot", 
    "cost": 4, 
    "drink": "coffee", 
    "next": {
        "rel": "http://localhost:3000/payment", 
        "uri": "http://localhost:3000/payment/order/1409071075510"
    }
}
GET /order/1409071075510 HTTP/1.1
Host: localhost:3000
Accept: application/json
router.get('/order/:id', function(req, res) {
  var id = req.param('id');
  var order = store.get(id);

  res.status(200).send(order);
});

http OPTIONS localhost:3000/payment/order/1409071075510
HTTP/1.1 200 OK
Allow: GET,PUT
Connection: keep-alive
Content-Length: 7
Content-Type: text/html; charset=utf-8
Date: Tue, 26 Aug 2014 16:54:44 GMT

GET,PUT
OPTIONS /payment/order/1409071075510 HTTP/1.1
Host: localhost:3000
http PUT localhost:3000/payment/order/1409071075510 \
     --auth-type=digest \
     -a ahmad:123 \
     cardNo=123456789 expires="07/15" name="Ahmad Nassri" amount="4.00"
HTTP/1.1 201 Created
Connection: keep-alive
Content-Length: 151
Content-Type: application/json; charset=utf-8
Date: Tue, 26 Aug 2014 16:53:07 GMT

{
    ...

    "payment": {
        "amount": "4.00", 
        "cardNo": "123456789", 
        "expires": "07/15", 
        "name": "Ahmad Nassri"
    }
}
PUT /payment/order/1409071075510 HTTP/1.1
Host: localhost:3000
Content-Type: application/json; charset=utf-8
Authorization: Digest username="ahmad" realm="localhost" nonce="..." ...

{
    "amount": "4.00", 
    "cardNo": "123456789", 
    "expires": "07/15", 
    "name": "Ahmad Nassri"
}
router.put('/payment/order/:id', function(req, res) {
  var id = req.param('id');
  var order = store.get(id);

  // modify order
  order.payment = {
    cardNo: req.body.cardNo,
    expires: req.body.expires,
    name: req.body.name,
    amount: req.body.amount
  };

  // save order
  store.set(id, order);

  // status
  res.status(201);

  // headers
  res.set({'Location': 'http://' + req.get('host') + '/order/' + id});

  res.send(order);
});

github.com/ahmadnassri/get-express-coffee

Hypermedia Hype

Collection+JSON

Hypertext Application Language (HAL)

GET /player/1234567890 HTTP/1.1

{
  "collection": {
    "version": "1.0",
    "href": "https://api.example.com/player",
    "items": [
      {
        "href": "https://api.example.com/player/1234567890",
        "data": [
            {"name": "playerId", "value": "1234567890", "prompt": "Identifier"},
            {"name": "name", "value": "Kevin Sookocheff", "prompt": "Full Name"},
            {"name": "alternateName", "value": "soofaloofa", "prompt": "Alias"}
        ],
        "links": [{
            "rel": "friends", 
            "href": "https://api.example.com/player/1234567890/friends", 
            "prompt": "Friends" 
        },{
            "rel": "image", 
            "href": "https://api.example.com/player/1234567890/avatar.png", 
            "prompt": "Avatar", 
            "render": "image"
          }
        ]
      }
    ]
  }
}

Collection+JSON

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890/friends HTTP/1.1

{
  "collection":
  {
    "version": "1.0",
    "href": "https://api.example.com/player/1234567890/friends",
    "links": [
      {"rel": "next", "href": "https://api.example.com/player/1234567890/friends?page=2"}
    ],
    "items": [
      {
        "href": "https://api.example.com/player/1895638109",
        "data": [
            {"name": "playerId", "value": "1895638109", "prompt": "Identifier"},
            {"name": "name", "value": "Sheldon Dong", "prompt": "Full Name"},
            {"name": "alternateName", "value": "sdong", "prompt": "Alias"}
        ],
        "links": [
          {"rel": "image", "href": "https://api.example.com/player/1895638109/avatar.png", "prompt": "Avatar", "render": "image" },
          {"rel": "friends", "href": "https://api.example.com/player/1895638109/friends", "prompt": "Friends" }
        ]
      },
      {
        "href": "https://api.example.com/player/8371023509",
        "data": [
            {"name": "playerId", "value": "8371023509", "prompt": "Identifier"},
            {"name": "name", "value": "Martin Liu", "prompt": "Full Name"},
            {"name": "alternateName", "value": "mliu", "prompt": "Alias"}
        ],
        "links": [
          {"rel": "image", "href": "https://api.example.com/player/8371023509/avatar.png", "prompt": "Avatar", "render": "image" },
          {"rel": "friends", "href": "https://api.example.com/player/8371023509/friends", "prompt": "Friends" }
        ]
      }
    ],
    "queries": [
      {
        "rel": "search", "href": "https://api.example.com/player/1234567890/friends/search", "prompt": "Search",
        "data": [
          {"name": "search", "value": ""}
        ]
      }
    ],
    "template": {
      "data": [
        {"name": "playerId", "value": "", "prompt": "Identifier" },
        {"name": "name", "value": "", "prompt": "Full Name"},
        {"name": "alternateName", "value": "", "prompt": "Alias"},
        {"name": "image", "value": "", "prompt": "Avatar"}
      ]
    }
  }
}

Collection+JSON

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890 HTTP/1.1

{
    "_links": {
        "self": { 
            "href": "https://api.example.com/player/1234567890" 
        },
        "curies": [{
            "name": "ex", 
            "href": "https://api.example.com/docs/rels/{rel}", 
            "templated": true 
        }],
        "ex:friends": { 
            "href": "https://api.example.com/player/1234567890/friends" 
        }
    },
    "playerId": "1234567890",
    "name": "Kevin Sookocheff",
    "alternateName": "soofaloofa",
    "image": "https://api.example.com/player/1234567890/avatar.png"
}

HAL

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890/friends HTTP/1.1

{
    "_links": {
        "self": { "href": "https://api.example.com/player/1234567890/friends" },
        "next": { "href": "https://api.example.com/player/1234567890/friends?page=2" }
    },
    "size": "2",
    "_embedded": { 
        "player": [
            { 
                "_links": { 
                    "self": { "href": "https://api.example.com/player/1895638109" },
                    "friends": { "href": "https://api.example.com/player/1895638109/friends" }
                },
                "playerId": "1895638109",
                "name": "Sheldon Dong",
                "alternateName": "sdong",
                "image": "https://api.example.com/player/1895638109/avatar.png"
            },
            { 
                "_links": { 
                    "self": { "href": "https://api.example.com/player/8371023509" },
                    "friends": { "href": "https://api.example.com/player/8371023509/friends" }
                },
                "playerId": "8371023509",
                "name": "Martin Liu",
                "alternateName": "mliu",
                "image": "https://api.example.com/player/8371023509/avatar.png"
            }
        ]
    }
}

HAL

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890 HTTP/1.1

{
    "@context": {
        "@vocab": "https://schema.org/",
        "image": { "@type": "@id" },
        "friends": { "@type": "@id" }
    },
    "@id": "https://api.example.com/player/1234567890",
    "playerId": "1234567890",
    "name": "Kevin Sookocheff",
    "alternateName": "soofaloofa",
    "image": "https://api.example.com/player/1234567890/avatar.png",
    "friends": "https://api.example.com/player/1234567890/friends"
}

JSON-LD

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890/friends HTTP/1.1

{
    "@context": [
        "http://www.w3.org/ns/hydra/core",
        {
            "@vocab": "https://schema.org/",
            "image": { "@type": "@id" },
            "friends": { "@type": "@id" }
        }
    ],
    "@id": "https://api.example.com/player/1234567890/friends",
    "operation": {
        "@type": "BefriendAction",
        "method": "POST",
        "expects": {
            "@id": "http://schema.org/Person",
            "supportedProperty": [
                { "property": "name", "range": "Text" },
                { "property": "alternateName", "range": "Text" },
                { "property": "image", "range": "URL" }
            ]
        }
    },
    "member": [
        {
            "@id": "https://api.example.com/player/1895638109",
            "name": "Sheldon Dong",
            "alternateName": "sdong",
            "image": "https://api.example.com/player/1895638109/avatar.png",
            "friends": "https://api.example.com/player/1895638109/friends"
        },
        {
            "@id": "https://api.example.com/player/8371023509",
            "name": "Martin Liu",
            "alternateName": "mliu",
            "image": "https://api.example.com/player/8371023509/avatar.png",
            "friends": "https://api.example.com/player/8371023509/friends"
        }
    ],
    "nextPage": "https://api.example.com/player/1234567890/friends?page=2"
}

JSON-LD + Hydra

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890 HTTP/1.1

{
    "class": "player",
    "links": [
        { "rel": [ "self" ], "href": "https://api.example.com/player/1234567890" },
        { "rel": [ "friends" ], "href": "https://api.example.com/player/1234567890/friends" }
    ],
    "properties": {
        "playerId": "1234567890",
        "name": "Kevin Sookocheff",
        "alternateName": "soofaloofa",
        "image": "https://api.example.com/player/1234567890/avatar.png"
    }
}

SIREN

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

GET /player/1234567890/friends HTTP/1.1

{
    "class": "player",
    "links": [
        {"rel": [ "self" ], "href": "https://api.example.com/player/1234567890/friends"},
        {"rel": [ "next" ], "href": "https://api.example.com/player/1234567890/friends?page=2"}
    ],
    "actions": [{
        "class": "add-friend",
        "href": "https://api.example.com/player/1234567890/friends",
        "method": "POST",
        "fields": [
            {"name": "name", "type": "string"},
            {"name": "alternateName", "type": "string"},
            {"name": "image", "type": "href"}
        ]
    }],
    "properties": {
        "size": "2"
    },
    "entities": [
        { 
            "links": [ 
                {"rel": [ "self" ], "href": "https://api.example.com/player/1895638109"},
                {"rel": [ "friends" ], "href": "https://api.example.com/player/1895638109/friends"}
            ],
            "properties": {
                "playerId": "1895638109",
                "name": "Sheldon Dong",
                "alternateName": "sdong",
                "image": "https://api.example.com/player/1895638109/avatar.png"
            }
        },
        { 
            "links": [
                {"rel": [ "self" ], "href": "https://api.example.com/player/8371023509"},
                {"rel": [ "friends" ], "href": "https://api.example.com/player/8371023509/friends" }
            ],
            "properties": {
                "playerId": "8371023509",
                "name": "Martin Liu",
                "alternateName": "mliu",
                "image": "https://api.example.com/player/8371023509/avatar.png"
            }
        }
    ]
}

SIREN

Kevin Sookocheff http://sookocheff.com/posts/2014-03-11-on-choosing-a-hypermedia-format/

TL;DR

  • JSON-LD
    • great for augmenting existing APIs.
    • mostly serves as documentation 
    • to add operations use HYDRA.
  • HYDRA
    • adds a vocabulary for communicating.
    • decouples API serialization format from the communication format.
  • HAL
    • light weight syntax & easy semantics
    • lack of support for specifying actions
  • Collection+JSON
    • best for representing data collections
    • list queries
    • templates
  • SIREN
    • generic classes of items
    • support for actions

API Security

"API Security Trooper" Credits: https://flic.kr/p/fJEXeX

TLS/SSL

  • SSL = Secure Socket Layer
  • TLS = Transport Layer Security
  • Protects against:
    • sniffing transaction requests
    • capture customer API keys
    • capture application API keys

Self-Signed vs Authority Signed

openssl genrsa -out key.pem
openssl req -new -key key.pem -out csr.pem
openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
  • generating a cirtificate is easy




     
  • Trusted Certificate Authority (Verisign, GeoTrust, Thawte) have their root keys bundled with web browsers and operating systems.
  • Expensive! USD $100 - $1000

Authentication

RFC 2617

# URL Versioning:

GET /api/v2/foo HTTP/1.1

# Custom Header:

GET /api/foo
X-VERSION: 2

# Content Type

GET: /api/foo
Accept: application/vnd.github.v3.raw+json
  • URLs
    • a URL should represent the resource, not a version
  • Custom Headers
    • not a semantic way of describing the resource
  • Accept headers
    • harder to test

Versioning

APIs are products!

NOT simply a medium:

AWS, Twilio, Facebook, IFTTT

Document & Monetise

Yeoman Tooling Hackathon

www.yeoman.to

Google Glass Reunion - One Year Later Panel Discussion
meetu.ps/2tTVkl

Thank you

Stalk me on Twitter @AhmadNassri, Read more on my Blog, Follow my APIs on Mashape.com/AhmadNassri.

http://www.ahmadnassri.com
http://www.mashape.com
http://www.yeoman.to
http://www.fbto.ca

API Love Story

By Ahmad Nassri

API Love Story

  • 1,949