restjsonapis-120711135859-phpapp02

Upload: onetxxl

Post on 03-Apr-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    1/77

    Beautiful REST + JSON APIs

    Les Hazlewood

    CTO, Stormpath

    stormpath.com

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    2/77

    About

    CTO Co-Founder StormpathIdentity and Access Management APIUser security automations (e.g.

    authentication, password reset)

    Active Directory for the cloudon steroids

    Founder, PMC Chair, Apache ShiroLeading Java Security Framework+200k downloads

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    3/77

    Outline

    APIs, REST & JSON REST Fundamentals Design:

    Base URL Versioning Resource Format

    Return Values Content Negotiation References (Linking) Pagination Query Parameters Associations Errors IDs Method Overloading

    Resource Expansion Partial Responses Caching & Etags Security Multi Tenancy Maintenance

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    4/77

    APIs

    Applications Developers Pragmatism over Ideology Adoption Scale

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    5/77

    Why REST?

    Scalability Generality Independence Latency (Caching) Security Encapsulation

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    6/77

    Why JSON?

    Ubiquity Simplicity Readability Scalability Flexibility

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    7/77

    HATEOAS

    Hypermedia As The Engine Of Application StateFurther restriction on REST architectures.

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    8/77

    REST Is Easy

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    9/77

    REST Is *&@#$! Hard(for providers)

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    10/77

    REST can be easy

    (if you follow some guidelines)

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    11/77

    Fundamentals

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    12/77

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    13/77

    Resources

    Nouns, not Verbs

    Coarse Grained, not Fine Grained

    Architectural style for use-case scalability

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    14/77

    /getAccount

    /createDirectory

    /updateGroup

    /verifyAccountEmailAddress

    What If?

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    15/77

    /getAccount/getAllAccounts/searchAccounts/createDirectory

    /createLdapDirectory/updateGroup/updateGroupName/findGroupsByDirectory/searchGroupsByName

    /verifyAccountEmailAddress/verifyAccountEmailAddressByTokenSmells like bad RPC. DONT DO THIS.

    What If?

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    16/77

    Keep It Simple

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    17/77

    The Answer

    Fundamentally two types of resources:

    Collection Resource

    Instance Resource

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    18/77

    Collection Resource

    /applications

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    19/77

    Instance Resource

    /applications/a1b2c3

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    20/77

    Behavior

    GETPUTPOSTDELETEHEAD

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    21/77

    Behavior

    POST, GET, PUT, DELETE

    1:1Create,Read,Update,Delete

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    22/77

    Behavior

    Asyouwouldexpect:

    GET=Read

    DELETE=Delete

    HEAD=Headers,noBody

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    23/77

    Behavior

    Notsoobvious:

    PUTandPOSTcanbothbeusedfor

    CreateandUpdate

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    24/77

    PUT for Create

    Idenfierisknownbytheclient:

    PUT /applications/clientSpecifiedId

    {

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    25/77

    PUT for Update

    FullReplacement

    PUT /applications/existingId{

    name: Best App Ever,

    description: Awesomeness

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    26/77

    PUT

    Idempotent

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    27/77

    POST as CreateOnaparentresource

    POST /applications

    {name: Best App Ever

    }

    Response:

    201 Created

    Location: https://api.stormpath.com/applications/a1b2c3

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    28/77

    POST as UpdateOninstanceresource

    POST /applications/a1b2c3

    {name: Best App Ever. Srsly.

    }

    Response:

    200 OK

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    29/77

    POST

    NOTIdempotent

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    30/77

    Media Types

    FormatSpecificaon+ParsingRules Request:Acceptheader Response:Content-Typeheader application/json application/foo+json application/foo+json;application

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    31/77

    Design Time!

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    32/77

    Base URL

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    33/77

    http(s)://api.foo.com

    vs

    http://www.foo.com/dev/service/api/rest

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    34/77

    http(s)://api.foo.com

    Rest Clientvs

    Browser

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    35/77

    Versioning

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    36/77

    URL

    https://api.stormpath.com/v1

    vs.

    Media-Typeapplication/json+foo;application,v=1

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    37/77

    Resource Format

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    38/77

    Media Type

    Content-Type: application/json

    When time allows:

    application/foo+json

    application/foo+json;bar=baz,v=1

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    39/77

    camelCase

    JS in JSON = JavaScript

    myArray.forEach

    NotmyArray.for_each

    account.givenName

    Notaccount.given_name

    Underscores for property/function namesare unconventional for JS. Stay consistent.

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    40/77

    Date/Time/Timestamp

    Theres already a standard. Use it: ISO 8601

    Example:

    {

    ,

    createdTimestamp: 2012-07-10T18:02:24.343Z

    }

    Use UTC!

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    41/77

    HREF

    Distributed Hypermedia is paramount! Every accessible Resource has a unique

    URL. Replaces IDs (IDs exist, but are opaque).{href: https://api.stormpath.com/v1/accounts/x7y8z9,

    }

    Critical for linking, as well soon see

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    42/77

    Response Body

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    43/77

    GET obvious

    What about POST?

    Return the representation in the responsewhen feasible.

    Add override (?_body=false) for control

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    44/77

    Content Negotiation

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    45/77

    Header

    Accept header Header values comma delimited inorder of preferenceGET /applications/a1b2c3

    Accept: application/json, text/plain

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    46/77

    Resource Extension

    /applications/a1b2c3.json

    /applications/a1b2c3.csv

    ConvenonallyoverridesAcceptheader

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    47/77

    Resource Referencesaka Linking

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    48/77

    Hypermedia is paramount. Linking is fundamental to scalability. Tricky in JSON XML has it (XLink), JSON doesnt How do we do it?

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    49/77

    Instance Reference

    GET /accounts/x7y8z9

    200 OK

    {

    href: https://api.stormpath.com/v1/accounts/x7y8z9,

    givenName: Tony,

    surname: Stark,

    ,

    directory: ????}

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    50/77

    Instance Reference

    GET /accounts/x7y8z9

    200 OK

    {

    href: https://api.stormpath.com/v1/accounts/x7y8z9,

    givenName: Tony,

    surname: Stark,

    ,

    directory: {href: https://api.stormpath.com/v1/directories/g4h5i6

    }

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    51/77

    Collection Reference

    GET /accounts/x7y8z9

    200 OK

    {

    href: https://api.stormpath.com/v1/accounts/x7y8z9,

    givenName: Tony,

    surname: Stark,

    ,

    groups: {

    href: https://api.stormpath.com/v1/accounts/x7y8z9/groups

    }

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    52/77

    Reference Expansion

    (aka Entity Expansion, Link Expansion)

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    53/77

    Account and its Directory?

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    54/77

    GET /accounts/x7y8z9?expand=directory

    200 OK

    {

    href: https://api.stormpath.com/v1/accounts/x7y8z9,

    givenName: Tony,

    surname: Stark,

    ,directory: {

    href: https://api.stormpath.com/v1/directories/g4h5i6,

    name: Avengers,

    creationDate: 2012-07-01T14:22:18.029Z,

    }

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    55/77

    Partial Representations

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    56/77

    GET /accounts/x7y8z9?fields=givenName,surname,directory(name)

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    57/77

    Pagination

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    58/77

    Collection Resource supports queryparams:

    Off

    set Limit/applications?offset=50&limit=25

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    59/77

    GET /accounts/x7y8z9/groups

    200 OK

    {href: /accounts/x7y8z9/groups,offset: 0,

    limit: 25,first: { href: /accounts/x7y8z9/groups?offset=0 },previous: null,

    next: { href: /accounts/x7y8z9/groups?offset=25 },

    last: { href: },items: [{href:

    },{

    href:

    },

    ]}

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    60/77

    Many To Many

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    61/77

    Group to Account

    A group can have many accounts An account can be in many groups Each mapping is a resource:GroupMembership

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    62/77

    GET /groupMemberships/23lk3j2j3

    200 OK

    {

    href: /groupMemberships/23lk3j2j3,

    account: {

    href:

    },group: {

    href:

    },

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    63/77

    GET /accounts/x7y8z9

    200 OK

    {

    href: /accounts/x7y8z9,

    givenName: Tony,

    surname: Stark,

    ,groupMemberships: {

    href: /groupMemberships?accountId=x7y8z9

    }

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    64/77

    Errors

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    65/77

    As descriptive as possible As much information as possible Developers are your customers

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    66/77

    POST /directories

    409 Conflict{status: 409,

    code: 40924,

    property: name,

    message: A Directory named Avengersalready exists.,

    developerMessage: A directory namedAvengers already exists. If you have a stalelocal cache, please expire it now.,

    moreInfo: https://www.stormpath.com/docs/api/errors/40924

    }

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    67/77

    IDs

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    68/77

    IDs should be opaque Should be globally unique

    Avoid sequential numbers (contention) Good candidates: UUIDs, Url64

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    69/77

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    70/77

    POST /accounts/x7y8z9?_method=DELETE

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    71/77

    Caching &Concurrency Control

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    72/77

    Server(inialresponse):

    ETag: "686897696a7c876b7e

    Client(laterrequest):

    If-None-Match: "686897696a7c876b7e

    Server(laterresponse):

    304 Not Modified

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    73/77

    Security

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    74/77

    Avoid sessions when possibleAuthenticate every request if necessaryStateless

    Authorize based on resource content, NOT URL!

    Use Existing Protocol:Oauth 1.0a, Oauth2, Basic over SSL only

    Use Custom Scheme:Only if you provide client code / SDKOnly if you really, reallyknow what youre doing

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    75/77

    Maintenance

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    76/77

    Use HTTP Redirects

    Create abstraction layer / endpoints whenmigrating

    Use well defined custom Media Types

  • 7/28/2019 restjsonapis-120711135859-phpapp02

    77/77

    Thanks!

    Check out Stormpath.com/Docs forreferences and examples

    Sign up at Stormpath.com Follow us on @goStormpath