Slide 1

Slide 1 text

Web Frontend, API Backend Lorna Mitchell, ZendCon 2014

Slide 2

Slide 2 text

Play along! Code is here: http://lrnja.net/backfront

Slide 3

Slide 3 text

Why Would You Do That?

Slide 4

Slide 4 text

Frontend / Backend Benefits • scalability • reusability • separation

Slide 5

Slide 5 text

Service-Oriented Architecture

Slide 6

Slide 6 text

How Would You Do That?

Slide 7

Slide 7 text

How Would You Do That? Hint: using the skills you already have

Slide 8

Slide 8 text

Model-View-Controller

Slide 9

Slide 9 text

Model-View-Controller

Slide 10

Slide 10 text

Slim Framework http://www.slimframework.com/ • modern microframework • offers routing out of the box • lightweight • extensible • actively developed and well supported

Slide 11

Slide 11 text

API-First

Slide 12

Slide 12 text

API Endpoints Our API will have two endpoints initially: • a list of events /events • an individual event /events/42 Using Slim Framework, let's put those in place

Slide 13

Slide 13 text

One Request

Slide 14

Slide 14 text

Events List Controller Slim registers a callback per route 24 $app->get('/events', function () use ($app) { 25 $db = $app->config('container')['db']; 26 $data = array(); 27 28 $model = new EventModel($db); 29 $data['events'] = $model->getSomeEvents(); 30 31 $app->render("foo.php", array("mydata" => $data)); 32 }); 33

Slide 15

Slide 15 text

Events List Model 1 public function getSomeEvents() { 2 // get all future events 3 $sql = "select ID, event_name, event_loc, event_desc " 4 . "from events " 5 . "where event_start > :start " 6 . "order by event_start " 7 . "limit 10"; 8 9 $stmt = $this->db->prepare($sql); 10 $stmt->execute(array("start" => mktime(0,0,0))); 11 $results = $stmt->fetchAll(PDO::FETCH_ASSOC); 12 return $results; 13 } 14

Slide 16

Slide 16 text

Output Handler 1 response->headers->set('Content-Type', 7 'application/json'); 8 return json_encode($this->data['mydata']); 9 } 10 }

Slide 17

Slide 17 text

Fetching the Events List GET http://localhost:8880/events { "events": [ { "ID": "44", "event_desc": "Praesent rutrum orci eget ipsum ornare et consequat neque egestas. Praesent rutrum orci eget ipsum ornare et consequat neque egestas. Lo "event_loc": "Bridgeport", "event_name": "CentOS day" }, { "ID": "6", "event_desc": "Vivamus gravida, dolor ut porta bibendum, mauris ligula condimentum est, id facilisis ante massa a justo. Nulla faucibus mollis ipsum si "event_loc": "Nectar", "event_name": "Ruby day" }, { "ID": "48", "event_desc": "Etiam ligula elit, condimentum lacinia fermentum nec, elementum id urna. Praesent rutrum orci eget ipsum ornare et consequat neque egest "event_loc": "New Hope", "event_name": "IT meetup" }, ...

Slide 18

Slide 18 text

Now The Frontend

Slide 19

Slide 19 text

Frontend Building Blocks This project is quick-started with: • Slim Framework again http://www.slimframework.com • Guzzle http://guzzlephp.org • PureCSS http://purecss.io/ (including their sample layout) • Brightened up with a little something from https://en.gravatar.com/

Slide 20

Slide 20 text

Event List

Slide 21

Slide 21 text

One Request

Slide 22

Slide 22 text

Events List Controller Slim registers a callback per route 16 $app->get('/', function () use ($app) { 17 $client = new ApiClient(new GuzzleHttp\Client()); 18 $events = $client->getEventList(); 19 $app->render("index.php", array("events" => $events)); 20 }); 21

Slide 23

Slide 23 text

Api Client 4 class ApiClient { 5 protected $client; 6 7 public function __construct(GuzzleHttp\Client $client) { 8 $this->client = $client; 9 } 10 11 public function getEventList() { 12 // todo make this URL configurable 13 $response = $this->client->get( 14 "http://localhost:8880/events"); 15 return $response->json(); 16 } 17

Slide 24

Slide 24 text

Template 34 35 36 37

38 avatar 40 44 45 46 47

Slide 25

Slide 25 text

The Gravatar Code Again To get one of those pattern things, the URL is: http://www.gravatar.com/avatar/[hash] ?d=identicon

Slide 26

Slide 26 text

Event Detail Page Made in the same way

Slide 27

Slide 27 text

Is That Everything?

Slide 28

Slide 28 text

Additional Considerations • identifying consumers • logging users in • picking a data format • documentation and testing

Slide 29

Slide 29 text

Identifying Consumers How open should your API be? Identify consumers to: • control access to resources • enable rate-limiting • track how the API is being used

Slide 30

Slide 30 text

How To Identify Consumers 1. give them an API key 2. check the API key on all requests

Slide 31

Slide 31 text

Logging Users In Please use a standard! Tokens are nicer than credentials

Slide 32

Slide 32 text

OAuth 2 OAuth2 solves trust and authentication issues between user, server and client.

Slide 33

Slide 33 text

How OAuth Works Consumer sends Authorization Grant to server. (Full details at http://tools.ietf.org/html/rfc6749) Server supplies an access token in response

Slide 34

Slide 34 text

OAuth2 Authorization Grants Authorization Code For untrusted clients; send user to website to log in, return an auth code Implicit Give the client an access token instead of an API key Client Credentials Use your own credentials as an access token Resource Owner Password Credentials Client exchanges user creds for an access token and only stores that

Slide 35

Slide 35 text

Implementing Login Start with a login form

Slide 36

Slide 36 text

Login Flow

Slide 37

Slide 37 text

Authorizations Endpoint 48 $app->post('/authorizations', function () use ($app) { 49 $db = $app->config('container')['db']; 50 $data = array(); 51 52 // horribly assuming JSON. Real code checks first 53 $in = json_decode(file_get_contents("php://input"), true); 54 55 $model = new AuthModel($db); 56 $data['access_token'] = $model->getAccessTokenFromCreds( 57 $in['consumer'], $in['username'], $in['password']); 58 $app->render("foo.php", array("mydata" => $data)); 59 }); 60

Slide 38

Slide 38 text

Making the Access Token 11 public function getAccessTokenFromCreds( 12 $consumer, $username, $password) { 13 14 $sql = "select ID, username, password from user " 15 . "where username = :username " 16 . "and password = :password "; 17 18 $stmt = $this->db->prepare($sql); 19 $stmt->execute(array("username" => $username, 20 "password" => md5($password))); 21 $user_info = $stmt->fetch(PDO::FETCH_ASSOC); 22

Slide 39

Slide 39 text

Making the Access Token 25 $token_sql = "insert into oauth_access_tokens " 26 . "set consumer_key = :consumer, " 27 . "user_id = :user_id, " 28 . "access_token = :token "; 29 30 // get random number and hash it for token 31 $token = bin2hex(openssl_random_pseudo_bytes(16)); 32 $token_stmt = $this->db->prepare($token_sql); 33 $token_stmt->execute(array( 34 "user_id" => $user_info['ID'], 35 "consumer" = $consumer, "token" => $token)); 36 return $token; 37 } 38

Slide 40

Slide 40 text

Using The Access Token

Slide 41

Slide 41 text

Data Formats • JSON or XML? • use Content-Type to specify which • be specific by using "Media Types"

Slide 42

Slide 42 text

Media Types Take the Content-Type header to the next level! Consider these: • application/json • application/vnd.github+json • application/vnd.github.v3+json https://en.wikipedia.org/wiki/Internet_media_type

Slide 43

Slide 43 text

Hypermedia • add links to related resources • for bonus points, use a standard like HAL { "_links": { "self": { "href": "/things/42" }, "messages": {"href": "/things/42/messages" }, "user": { "href": "/users/123" } } } http://stateless.co/hal_specification.html

Slide 44

Slide 44 text

Documentation Yes

Slide 45

Slide 45 text

Documentation Lots of options: • old-fashioned, write words and copy/paste examples • static plus something like http://hurl.it • interactive docs: try https://github.com/mashery/iodocs • http://apiary.io/ produces a mock API, then verifies your real one

Slide 46

Slide 46 text

What Else?

Slide 47

Slide 47 text

Frontend, Backend • independently scalable/deployable/maintainable • reusable backend • modular systems • flexible when requirements change

Slide 48

Slide 48 text

Questions? While this slide is up, there may be interest in: • Feedback https://m.joind.in/talk/1af2a (from http://oreilly.com) • my blog: http://lornajane.net • joind.in itself http://m.joind.in/about