CakeFest 2013 - A-Z REST APIs

Post on 12-Jun-2015

143 views 2 download

Tags:

Transcript of CakeFest 2013 - A-Z REST APIs

A-Z R

EST APIS

USING C

AKEPHP

ANTH

ON

Y PU

T IGN

ANO

VP OF E

NG

INEER IN

G @

ABOU

T.ME /A

NTH

ON

Y.PU

T IGN

ANO

DATACOLLABORATI

ON

NEW PLATFORM

• Backend REST API built using CakePHP

• Web client consumes the API via a thin Node.js server and a single-page AngularJS app

• Mobile client consumes the API with a PhoneGap-wrapped Sencha Touch app

MISCONCEPTION:

"Building a REST API will be easy! All we need to do is hook up our controllers to a bunch of CRUD actions, serve them out like they exist in the database, and figure out a way to communicate in JSON/XML format!

Alright, is it happy hour yet!?”

IN REALITY…

• Input/Output data purification

• Permissions

• Stateless authentication & authorization

• Cross-Origin Resource Sharing (CORS)

• Documentation

… and more lurking around the corner.

API SCHEMA !== DATABASE SCHEMA• Many attribute names don’t match

column names

• Some attributes don’t map cleanly to columnar data

• Some resources don’t map cleanly to database tables

• API consumers expect a lot of related data to be accessible in a single request

MODELS AREN’T JUST FOR TABLES• In a traditional app, a model generally

represents a table

• In an API, a model represents a resource

• New strategy• Share models when possible•Create new models for API-only resources• Set up attributes & relations for each API-accessible model

CONFIGURE MODEL ATTRIBUTES

SET UP API RELATIONS

INPUT & OUTPUT PROCESSING• We now have at least 1 model for every API

resource

• We now have information about the attributes each API resource outputs

• We now have information about the relations each API resource relies on by default vs. by request

• We use this to input & output data automagically

How…?

INPUT DATA COMPONENTWhat does it do?

CONVERT TO COLUMNAR DATA• API request is submitted using API

resource & attribute names

• When processing this request, convert the request into column names that can be saved to tables

CONVERT OPTIONS TO INTEGERS• Options are friendly on the API

consumer:`status` = “complete”

• Integers are friendly on the database:`status` = 1

• Don’t compromise one for the other – convert on input

HONOR TYPECASTING• Use attribute configurations to

determine data type

• Convert ISO 8601 formatted dates

• Convert “false” string to`false` for booleans

• Convert “null” string to `NULL` for NULLables

• etc

INTEGRATE FOREIGN KEYS• /forms parent resource

• /forms/{form.id}/records -> sub-resource

• When saving a record, automatically place `form_id` in POST data based on the value in the URL path

BRING IN DENORMALIZED DATA• Form belongs to a Workspace

• Record belongs to a Form

• Record has a denormalized `workspace_id` column for easy reference & querying

• User shouldn’t have to submit the Workspace ID if they’ve already declared the Form ID

QUERY COMPONENTWhat does it do?

CONVERT TO COLUMNAR DATA• API request is submitted using API

resource & attribute names

• When processing this request, convert the conditions into column names that the app can use with a `find`

CONVERT OPTIONS TO INTEGERS• Same `status` = “complete” -> `status`

= 1 conversion happens here

INTEGRATE FOREIGN KEYS• Same integration of data happens here

• A query to /forms/{form.id}/records leads to an automatic inclusion of the Form ID in the query conditions

PERMISSIONS• ACL is great for “static” permissions

• It’s not so great at handling “variable” permissions

… What’s the difference? How do we reconcile this?

STATIC PERMISSIONS• App-wide “groups” of users (ie. default,

admin, root)

• Allow or block CRUD access to an entire resource

• Allow or block CRUD access to a resource’s attribute

THE ACL IS OUR FRIEND$setActions is an anonymousfunction which sets up resourcerules that the `PermissionComponent`can understand.

$setCrud is an anonymousfunction which sets up attributerules.

Why anonymous functions?Hack to get past tough problems,but should be improved.

DYNAMIC PERMISSIONS• Groups created by a resource (ie.

workspace member, workspace owner, etc)

• Allow or block CRUD access to a resource based on dynamic group member

PERMISSIONS COMPONENT• canCreate() – returns boolean

• canUpdate() – returns boolean

• canDelete() – returns boolean

• requireConditions() – returns array of “safe” conditions based on requested query conditions + permitted access

MODEL AS THE GO-TO SOURCE• isUserAuthorizedToCreate() – returns

boolean

• isUserAuthorizedToUpdate() – returns boolean

• isUserAuthorizedToDelete() – returns boolean

• userIsAuthorizedToReadSomeFieldName() – returns array of values that the user is allowed to query by, or “*” if all

IS USER AUTHORIZED TO READ?

AUTHENTICATION & AUTHORIZATION• Various different protocols

• Many costs & benefits to each

• We decided on oAuth 2 because it:• is simple• accommodates many different types of clients• is being adopted by some major providers

OAUTH 2: THE SIMPLE VERSION:• Supports public (2-legged) & private (3-

legged) flows

• Uses an access token for 3-legged flows

WHAT ARE THE CHALLENGES?• Clients need a way to get & refresh

access tokens

• App needs to authenticate the user with every request (it is stateless)

OAUTH2 PLUGIN• There’s a lot to it, but basically…

• The `OAuth2Controller` handles all of the client’s token getting & refreshing needs via:• authorize()• grant()• token()

• The `OAuth2Authenticate` class is an authentication adapter which “logs the user in” on every request

CONFIGURATION? EASY.

CROSS-ORIGIN RESOURCE SHARING• API calls are often done from frontend

apps

• Browser will not less you make “cross-origin” (cross-domain) requests without extra request & response headers

• Cake doesn’t have any default options for this

CORS PREFLIGHT DISPATCHER• Uses Cake 2.x dispatch filters

• If CORS request headers exist, it outputs CORS response headers

• Automatically stops propagation on OPTIONS requests

• Complete solution; all CORS logic in one place

DOCUMENTATION• Should be as DRY as possible

• Should not sacrifice usability

• Interactive is better

• We chose Swagger UIhttps://github.com/wordnik/swagger-ui

HOW TO KEEP IT DRY?• `ApiDocsShell` - uses combination of:

•Routes•Models•Attribute configurations• Permissions•Validation rulesto automagically build interactive documentation about resources.

HOW TO KEEP IT USER FRIENDLY?• Sometimes a resource “dictionary” isn’t

enough

• Users need guidance

• Plain English descriptions help

• Pull those in based on convention•Attribute descriptions• Files which hold resource- & operation- level descriptions

WHAT ARE WE MISSING?• JSON & XML formatting

• Error handling

• Routing

• Versioning

• Caching

• Links

• Rate limiting

• Monetization

• … it never really ends. Pick your battles wisely. Outsource functions if you can:http://www.3scale.net/

OPEN SOURCED

github.com/Wizehive/cakephp-api-utils

Connect with me at:

about.me/anthony.putignano