Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Web Frontend, API Backend

Web Frontend, API Backend

Introduction to building a PHP application with separate front and back ends.

Lorna Mitchell

October 29, 2014
Tweet

More Decks by Lorna Mitchell

Other Decks in Technology

Transcript

  1. Web Frontend, API Backend
    Lorna Mitchell, ZendCon 2014

    View Slide

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

    View Slide

  3. Why Would You Do That?

    View Slide

  4. Frontend / Backend Benefits
    • scalability
    • reusability
    • separation

    View Slide

  5. Service-Oriented Architecture

    View Slide

  6. How Would You Do That?

    View Slide

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

    View Slide

  8. Model-View-Controller

    View Slide

  9. Model-View-Controller

    View Slide

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

    View Slide

  11. API-First

    View Slide

  12. 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

    View Slide

  13. One Request

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. Output Handler
    1 2
    3 class View extends \Slim\View {
    4 public function render($template) {
    5 $app = \Slim\Slim::getInstance();
    6 $app->response->headers->set('Content-Type',
    7 'application/json');
    8 return json_encode($this->data['mydata']);
    9 }
    10 }

    View Slide

  17. 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"
    },
    ...

    View Slide

  18. Now The Frontend

    View Slide

  19. 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/

    View Slide

  20. Event List

    View Slide

  21. One Request

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. Template
    34
    35
    36
    37 =$event['event_name']?>
    38 39 src="http://www.gravatar.com/avatar/=md5($event['ID'])?>?d=identicon">
    40
    41 More details: 42 href="/showEvent/=$event['ID']?>">click here
    43
    44
    45
    46
    47

    View Slide

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

    View Slide

  26. Event Detail Page
    Made in the same way

    View Slide

  27. Is That Everything?

    View Slide

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

    View Slide

  29. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. 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

    View Slide

  34. 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

    View Slide

  35. Implementing Login
    Start with a login form

    View Slide

  36. Login Flow

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. Using The Access Token

    View Slide

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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. Documentation
    Yes

    View Slide

  45. 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

    View Slide

  46. What Else?

    View Slide

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

    View Slide

  48. 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

    View Slide