Transcript
Page 1: REST API's: Easier Than You Imagined

REST API’s:Easier Than You Imagined

@AdamTuttleFusionGrokker.com

You are here

Page 2: REST API's: Easier Than You Imagined

Agenda

• REST 101

• Creating REST APIs with ColdFusion*

• Adding an API to an existing app

• Best Practices, Patterns, Anti-patterns

Page 3: REST API's: Easier Than You Imagined

Agenda

• REST 101

• Creating REST APIs with ColdFusion*

• Adding an API to an existing app

• Best Practices, Patterns, Anti-patterns

*Using Taffy

Page 4: REST API's: Easier Than You Imagined
Page 5: REST API's: Easier Than You Imagined

I’m @AdamTuttle

Page 6: REST API's: Easier Than You Imagined

That’s Me!

Page 7: REST API's: Easier Than You Imagined

I make stuff

Page 8: REST API's: Easier Than You Imagined

• Created May 2010

• #6 most watched ColdFusion project on GitHub

• 8+ people contributing

• Syntax doesn’t suck

I made Taffy

Page 9: REST API's: Easier Than You Imagined

Who Uses Taffy?

Page 10: REST API's: Easier Than You Imagined

REST 101

Page 11: REST API's: Easier Than You Imagined

What is REST?

Page 12: REST API's: Easier Than You Imagined

What is REST?

Page 13: REST API's: Easier Than You Imagined

Request ResponseGET http://...HeadersRequest data

200 OKHeadersResponse data

Page 14: REST API's: Easier Than You Imagined
Page 15: REST API's: Easier Than You Imagined

<form action=”http://...” method=”POST”>

</form>

Page 16: REST API's: Easier Than You Imagined

VocabularyVerb

Noun

Data Format (“Mime Type”)

Data

Page 17: REST API's: Easier Than You Imagined

HTTP VerbsGET Read

POST Insert

PUT Update

DELETE Delete

OPTIONS Allowed Verbs

HEAD Headers Only

Page 18: REST API's: Easier Than You Imagined

HTTP VerbsGET Read

POST Insert

PUT Update

DELETE Delete

OPTIONS Allowed Verbs

HEAD Headers Only

Safe

Unsafe

Idempotent

Page 20: REST API's: Easier Than You Imagined

<form action=”http://...” method=”POST”>

</form>

Page 21: REST API's: Easier Than You Imagined

<form action=”http://...” method=”POST”>

</form>

Noun

Page 22: REST API's: Easier Than You Imagined

<form action=”http://...” method=”POST”>

</form>

NounVerb

Page 23: REST API's: Easier Than You Imagined

<form action=”http://...” method=”POST”>

</form>

NounVerb

<input type=”hidden” name=”foo” value=”bar” />

Request Data

Page 24: REST API's: Easier Than You Imagined

Data FormatHTML: text/html

Image: image/jpeg

JSON: application/json

XML: application/xml

ZIP: application/octet-stream

Page 25: REST API's: Easier Than You Imagined

HTTP Status Codes

• 2xx = Success

• 3xx = Redirect

• 4xx = Client Error

• 5xx = Server Error

Page 26: REST API's: Easier Than You Imagined

Do Not Write This Down

This will not be on the test

Page 27: REST API's: Easier Than You Imagined

Raw Request$ telnet www.google.com 80Trying 173.194.73.104...Connected to www.google.com.Escape character is '^]'.GET /index.html HTTP/1.1Host: www.google.com{blank line}

Page 28: REST API's: Easier Than You Imagined

Raw ResponseHTTP/1.1 200 OKDate: Sat, 11 May 2013 18:29:45 GMT[...]X-XSS-Protection: 1; mode=blockX-Frame-Options: SAMEORIGINTransfer-Encoding: chunked

<!doctype html><html..........

}ResponseHeaders

Page 29: REST API's: Easier Than You Imagined

Web APIs work similarly

Page 30: REST API's: Easier Than You Imagined

Raw RequestPOST /api/v1/tweets HTTP/1.1Host: twitter.comContent-Type: application/jsonAccept: application/json, */*;q=0.9User-Agent: MyTwitterApp{

“username”: “AdamTuttle”,“tweet”: “Hello, #CFObjective!”

}[blank line]

Page 31: REST API's: Easier Than You Imagined

Raw ResponseHTTP/1.1 201 Tweet CreatedContent-Type: application/json; charset=UTF-8{

...}

Page 32: REST API's: Easier Than You Imagined

<form action=”http://.../foo/bar” method=”POST”>

</form>

<input type=”hidden” name=”foo” value=”bar” />

Page 33: REST API's: Easier Than You Imagined

POST /....../foo/barHost: www.example.comContent-Type: x-www-form-urlencodedAccept: application/json

foo=bar&flap=jacks

Page 34: REST API's: Easier Than You Imagined

POST /....../foo/barHost: www.example.comContent-Type: x-www-form-urlencodedAccept: application/json

foo=bar&flap=jacks

No question mark

Page 35: REST API's: Easier Than You Imagined

Creating REST APIs with ColdFusion*

*Using Taffy

Page 36: REST API's: Easier Than You Imagined

Choices• Taffy

• Mach-II

• ColdBox

• Relaxation

• Powernap

• RestfulCF

• CF10

Page 37: REST API's: Easier Than You Imagined

Why I (still) <3 Taffy• Simplest, Most-concise syntax

• Easily extended (security layer, etc)

• Doesn’t require access to CFAdmin

• Portable (Railo, CF8+ supported)

• Plays fine with any app framework

• ONLY Convention-over-Config option (CF10?)

• Open Source

Page 38: REST API's: Easier Than You Imagined

Your FirstTaffy-powered API

Page 39: REST API's: Easier Than You Imagined

Application.cfc

component extends=”taffy.core.api” {}

index.cfm

<!--- this space intentionally blank--->

Page 40: REST API's: Easier Than You Imagined
Page 41: REST API's: Easier Than You Imagined

An API w/ Results... that fits into a

Tweet

Page 42: REST API's: Easier Than You Imagined

Application.cfccomponent extends=”taffy.core.api”{}

index.cfm

resources/hi.cfccomponent extends = ”taffy.core.resource” taffy_uri = ”/hi”{ function get(){ return representationOf(“hi”); }}

Page 43: REST API's: Easier Than You Imagined

Application.cfccomponent extends=”taffy.core.api”{}

index.cfm

resources/hi.cfccomponent extends = ”taffy.core.resource” taffy_uri = ”/hi”{ function get(){ return representationOf(“hi”); }}

36

104

Page 44: REST API's: Easier Than You Imagined
Page 45: REST API's: Easier Than You Imagined
Page 46: REST API's: Easier Than You Imagined
Page 47: REST API's: Easier Than You Imagined
Page 48: REST API's: Easier Than You Imagined
Page 49: REST API's: Easier Than You Imagined

Anatomy of an API

Page 50: REST API's: Easier Than You Imagined

Anatomy of an API

Application.cfcindex.cfm/resources/...

Page 51: REST API's: Easier Than You Imagined

Application.cfccomponent extends=”taffy.core.api” {

variables.framework = {};

function applicationStartEvent(){} function requestStartEvent(){} function onTaffyRequest(){}

}

Page 52: REST API's: Easier Than You Imagined

index.cfm

Page 53: REST API's: Easier Than You Imagined

Resources

• Live in /resources subfolder• Each resource defines its own URI• Only implemented verbs are allowed• Collections vs. Members

Page 54: REST API's: Easier Than You Imagined

Resources

component extends=”taffy.core.resource”taffy_uri=”/foo/{fooId}” {

public function get(fooId){} public function delete(fooId){}

}

Page 55: REST API's: Easier Than You Imagined

3 Data Input Options

Page 56: REST API's: Easier Than You Imagined

Tokens & Query Params

• Mapped by name to method args

• Tokens are the same for every method in a resource CFC and always required (otherwise 404)

• Query Params are optional; also passed by name

Page 57: REST API's: Easier Than You Imagined

Simple Inputcomponent extends=”taffy.core.resource” taffy_uri=”/foo/{fooId}” {

function get(fooId, optional string city){ //... }}

<cfargument name=”city” required=”false” />

/api/index.cfm/foo/17?city=Philadelphia

Page 58: REST API's: Easier Than You Imagined

Simple Inputcomponent extends=”taffy.core.resource” taffy_uri=”/foo/{fooId}” {

function get(fooId, optional string city){ //... }}

<cfargument name=”city” required=”false” />

/api/index.cfm/foo/17?city=Philadelphia

Page 59: REST API's: Easier Than You Imagined

Simple Inputcomponent extends=”taffy.core.resource” taffy_uri=”/foo/{fooId}” {

function get(fooId, optional string city){ //... }}

<cfargument name=”city” required=”false” />

/api/index.cfm/foo/17?city=Philadelphia

Page 60: REST API's: Easier Than You Imagined

Simple Inputcomponent extends=”taffy.core.resource” taffy_uri=”/foo/{fooId}” {

function get(fooId, optional string city){ //... }}

<cfargument name=”city” required=”false” />

/api/index.cfm/foo/17?city=Philadelphia

Page 61: REST API's: Easier Than You Imagined

Simple Inputcomponent extends=”taffy.core.resource” taffy_uri=”/foo/{fooId}” {

function get(fooId, optional string city){ //... }}

<cfargument name=”city” required=”false” />

/api/index.cfm/foo/17?city=Philadelphia

Page 62: REST API's: Easier Than You Imagined

Request Body InputTaffy Supports JSON and Form-encoded bodies as long as you set the Content-Type header

{“foo”:”bar baz”,”baz”:true}

foo=bar%20baz&baz=true

Page 63: REST API's: Easier Than You Imagined

Data Output

Page 64: REST API's: Easier Than You Imagined

Returning DatarepresentationOf method abstracts serialization

component extends=”taffy.core.resource”taffy_uri=”/foo” {

public function get(){ var querySvc = new Query(); //... return representationOf( someQuery ); }

}

Page 65: REST API's: Easier Than You Imagined

Returning DatarepresentationOf method abstracts serialization

component extends=”taffy.core.resource”taffy_uri=”/foo” {

public function get(){ var querySvc = new Query(); //... return representationOf( myStruct ); }

}

Page 66: REST API's: Easier Than You Imagined

Returning DatarepresentationOf method abstracts serialization

component extends=”taffy.core.resource”taffy_uri=”/foo” {

public function get(){ var querySvc = new Query(); //... return representationOf( myArray ); }

}

Page 67: REST API's: Easier Than You Imagined

ColdFusion serializes queries ... abnormally.

{"COLUMNS":["COL1","COL2","COL3"],"DATA":[[3,3,true]]}

Taffy’s queryToArray() helper convertsqueries to an array of structures

[ {"CoL1":3,"col2":3,"col3":true}, {"CoL1":4,"col2":4,"col3":false}]

queryToArray()

Page 68: REST API's: Easier Than You Imagined

queryToArray()

return representationOf(queryToArray( myQuery )

);

Page 69: REST API's: Easier Than You Imagined

Response Headersreturn representationOf( ...).withStatus( 201, “Created”).withHeaders( { “X-MY-HEADER” = “My header value” });

Page 70: REST API's: Easier Than You Imagined

Empty Success

return noData().withStatus(201, “Created”);

return noData();

Page 71: REST API's: Easier Than You Imagined

More Advancedgithub.com/atuttle/Taffy/wiki

Page 72: REST API's: Easier Than You Imagined

Dependency Injection/resources/FooService.cfc

/resources/FooCollection.cfc

component { function doStuff(){...}; }

component extends=”taffy.core.resource” ...{ property name=”FooService”;

function get(){ local.foo = this.FooService.doStuff(); //... }}

Page 73: REST API's: Easier Than You Imagined

Resource Subfolders/resources/fooCollection.cfc “fooCollection”

/resources/Cat/Grumpy.cfc “CatGrumpy”

/resources/Services/Old/User.cfc “ServicesOldUser”

Bean Nam

e Generation

Page 74: REST API's: Easier Than You Imagined

• Custom Token Regex

• Intercept requests w/ onTaffyRequest

• 3rd Party Bean Factories

• Verb:Method mapping w/ metadata

• Custom result serializers

• Environment specific config

• ETags for result caching

• ... much, much, much more

Page 75: REST API's: Easier Than You Imagined

Adding an API to an existing App

Page 76: REST API's: Easier Than You Imagined

Shared State

• Subfolder supported, not required

• Enable by using the same Application Name

• Shared variables*

*with great power comes great responsibility

Page 77: REST API's: Easier Than You Imagined

Best Practices

Page 78: REST API's: Easier Than You Imagined

Separate Collections &

Members

Page 79: REST API's: Easier Than You Imagined

Version yourAPI from day 1.

/api/v1/foo/bar

Page 80: REST API's: Easier Than You Imagined

Thoughtful URIs

Page 81: REST API's: Easier Than You Imagined

Human-Readable URIs

Page 82: REST API's: Easier Than You Imagined

Token vsQuery String

Page 83: REST API's: Easier Than You Imagined

PUT or POST?

Page 84: REST API's: Easier Than You Imagined

HATEOAS

Page 85: REST API's: Easier Than You Imagined

HTTP Status Codes are your friend

Page 86: REST API's: Easier Than You Imagined

Antipatterns

Page 87: REST API's: Easier Than You Imagined

I hate you.

If you code like this...

Page 88: REST API's: Easier Than You Imagined

GET request writes data

Page 89: REST API's: Easier Than You Imagined

2+ requests / task

Page 90: REST API's: Easier Than You Imagined

200 Error

Page 91: REST API's: Easier Than You Imagined

Using Cookies

Page 92: REST API's: Easier Than You Imagined

Getting SupportMailing List: bit.ly/taffy-users

IRC: #coldfusion on freenode and dalnetTweet me: @AdamTuttle

Page 93: REST API's: Easier Than You Imagined

Thank You

Page 94: REST API's: Easier Than You Imagined