49
API MANAGEMENT WITH TAFFY AND API BLUEPRINT KAI KOENIG (@AGENTK)

API management with Taffy and API Blueprint

Embed Size (px)

Citation preview

Page 1: API management with Taffy and API Blueprint

API MANAGEMENT WITH TAFFY AND API BLUEPRINT

KAI KOENIG (@AGENTK)

Page 2: API management with Taffy and API Blueprint

AGENDA

▸ What is Taffy?

▸ REST in a nutshell

▸ But is it “RESTful ENOUGH”?

▸ Taffy setup and basic flow

▸ Advanced configuration

▸ API Blueprint and other tools

Page 3: API management with Taffy and API Blueprint

WHAT IS TAFFY?

Page 4: API management with Taffy and API Blueprint

WHAT IS TAFFY?

TAFFY

▸ TLDR version: Taffy is a framework to build REST-based APIs in CFML

▸ Developed by Adam Tuttle

▸ Currently in version 3.1, incepted around 2011 (MIT license)

▸ Very stable, very powerful

▸ Allows the creation of REST-based APIs in CFML independent of any vendor implementation - works with Lucee and ACF

▸ Can be integrated with other frameworks

Page 5: API management with Taffy and API Blueprint

WHAT IS TAFFY?

WHY TAFFY?

“But hey, both Lucee and Adobe CF have their own implementation of REST-API handling and it’s SO EASY to use and it has SELF-DOCUMENTATION! That’s great, ey?”

▸ At first glance it might seem like a great idea for getting started. But:

▸ Various issues (created URL structures, JSON handling, clunky syntax)

▸ Significantly lock-in to a vendor’s implementation

▸ Overall developer-unfriendly approaches

Page 6: API management with Taffy and API Blueprint

WHAT IS TAFFY?

BENEFITS OF TAFFY

▸ This is not a talk about the deficiencies of REST in ACF or Lucee

▸ This is why Taffy is great:

▸ Developer-friendly and easy to get started with

▸ Backwards-compatible to ACF 8

▸ Convention over configuration

▸ Hardly any boilerplate code

Page 7: API management with Taffy and API Blueprint

REST IN A NUTSHELL

https://www.flickr.com/photos/kikasz/2891113802/

Page 8: API management with Taffy and API Blueprint

REST IN A NUTSHELL

WHAT IS REST?

▸ REpresentational State Transfer - a method to communicate over a network

▸ Mostly used over HTTP, technically protocol-independent

▸ 2 phases:

▸ Request - noun, verb, MIME type, headers, data

▸ Response - status code, status text, headers, data

Page 9: API management with Taffy and API Blueprint

REST IN A NUTSHELL

THE REQUEST (I)

▸ Nouns - identifier of the data we want to work with, e.g. /users or /users/232

▸ Main verbs:

▸ POST: creating a new record (unsafe)

▸ PUT: updating existing record (idempotent)

▸ GET: retrieving existing record (safe)

▸ DELETE: deleting record (idempotent)

Page 10: API management with Taffy and API Blueprint

REST IN A NUTSHELL

THE REQUEST (II)

▸ Less commonly used verbs:

▸ HEAD: similar to GET, but only returns headers (safe)

▸ OPTIONS: used to check which verbs are available (safe)

▸ PATCH: similar to PUT, updating individual properties (idempotent)

▸ MIME type:

▸ Client and API server agree on the data types for exchange, e.g. text/html, application/xml etc

Page 11: API management with Taffy and API Blueprint

REST IN A NUTSHELL

THE REQUEST (III)

▸ Headers - supply metadata with the request (Accept, Content-Type)

▸ Data - the data sent from the client to the server

▸ Can conceptually be of any format

▸ Most common: JSON

Page 12: API management with Taffy and API Blueprint

REST IN A NUTSHELL

THE RESPONSE

▸ Status code & Status text:

▸ “200 OK”

▸ “404 Not Found”

▸ “500 Server Error” …

▸ There are a lot more specific status codes, such as 201, 301, 302, 400, 401 etc.

▸ Headers and Data

Page 13: API management with Taffy and API Blueprint

REST IN A NUTSHELL

WHY DO PEOPLE LIKE AND USE REST?

▸ Works naturally with the HTTP protocol

▸ Other alternatives:

▸ SOAP-XML: very enterprise-y, bloated and complex

▸ XML-RPC: similar to REST, but ignores the power of the verb

Page 14: API management with Taffy and API Blueprint

RESTFUL ENOUGH?

https://www.flickr.com/photos/kikasz/2891113802/https://xkcd.com/386/

Page 15: API management with Taffy and API Blueprint

BUT IS IT RESTFUL ENOUGH?

THE REALITY IS MORE COMPLEX

▸ Roy Fielding’s 2000 dissertation about what we today know as REST

▸ Followed up by a series of blog posts from ~2008 about HATEOAS (Hypermedia As The Engine Of Application State)

▸ prescribing additional requirements and behaviours

▸ often considered as ideal, but impractical in a lot of ways

▸ Leads to a lot of discussion what REST is and how one has to use it

Page 16: API management with Taffy and API Blueprint

BUT IS IT RESTFUL ENOUGH?

MORE MATURE?

▸ Plain JSON

▸ HATEOAS

{ "name" : “iPhone 7" }

{ "name": “iPhone 7", "links": [ { "rel": "self", "href": "http://localhost:8080/product/1" } ] }

Page 17: API management with Taffy and API Blueprint

BUT IS IT RESTFUL ENOUGH?

HATEOAS{ "content": [ { "price": 499.00, "name": "iPad", "links": [ { "rel": "self", "href": "http://localhost:8080/product/1" } ], "attributes": { "connector": "socket" } }, { "price": 49.00, "name": "Dock", "links": [ { "rel": "self", "href": "http://localhost:8080/product/3" } ], "attributes": { "connector": "plug" } } ], "links": [ { "rel": "product.search", "href": "http://localhost:8080/product/search" } ] }

Page 18: API management with Taffy and API Blueprint

BUT IS IT RESTFUL ENOUGH?

RICHARDSON MATURITY LEVEL

http://martinfowler.com/articles/richardsonMaturityModel.html

Page 19: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

https://www.flickr.com/photos/joanna_young/4515399787/

Page 20: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

INSTALLATION

▸ Download .zip-file from Github or taffy.io

▸ Installation options:

▸ Global mapping /taffy

▸ /taffy physically in webroot

▸ Installation in sub-directory of site (needs application-specific mappings)

Page 21: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

DRIVEN BY APPLICATION.CFC

▸ Extends taffy.core.api

▸ Needs to call onApplicationStart() and onRequestStart() on the parent CFC

▸ Earlier versions of Taffy had their own Application.cfc events - it was recommended to actually not implement onApplicationStart() etc

▸ That’s gone since Taffy 2.0

component extends="taffy.core.api" { this.name = "TaffyDemo";

function onApplicationStart() { application.productList = [ { "id":1, "name":"iPhone 7" }, { "id":2, "name":"iPhone 7+" }, { "id":3, "name":"iPad Pro 9.7inch" } ];

return super.onApplicationStart();

}

function onRequestStart() { return super.onRequestStart(); }

}

Page 22: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

CONVENTION OVER CONFIGURATION

▸ Taffy tries to NOT be in your way

▸ Examples:

▸ Default function names for resources are get(), post(), put(), delete()

▸ Looks for resources in a /resources directory

▸ Lots of things are just fine and work out of the box naturally because of sensible conventions

Page 23: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

MINIMAL LAYOUT

Page 24: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

RESOURCES (I)

▸ Extend taffy.core.resource

▸ Live in the /resources directory or a /resources mapping

▸ Can be changed - requires dropping the default bean factory

▸ Good practice:

▸ Have a CFC for a “thing” and for a “collection of things” each

▸ My convention: “SomeThing” and “SomeThingCollection”

▸ A resource should at least implement one verb function

Page 25: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

RESOURCES (II)

▸ taffy_uri defines the public URL of your resource

▸ representationOf() triggers the default serialiser

▸ withStatus() sets the HTTP status of the response (defaults to “200 OK”)

component extends="taffy.core.resource" taffy_uri="/product" { public function get(){

return representationOf(application.productList).withStatus(200); } }

Page 26: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

RESOURCES (III)

▸ Passing {productId} token into the resource’s URL

▸ Returning 404 if there’s no record found

component extends="taffy.core.resource" taffy_uri="/product/{productId}" { public function get(numeric productId){

for (var product in application.productList) { if (product["id"] == arguments.productId) { return representationOf(product).withStatus(200, "OK"); } }

return noData().withStatus(404, "Not Found"); } }

Page 27: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

RESOURCES (IV)

▸ If a verb is not implemented in a resource, Taffy will return “405 Not Allowed”

▸ Tokens will automatically become part of the arguments scope

▸ Tokens support regular expressions: /product/{pId:\d{4}} - /product/{pId:[A-Za-z]+}

▸ Query string parameters will ALSO automatically become part of the arguments scope

▸ Use .noData() if the intention is to not return data

▸ Similar to .withStatus(…), it’s possible to chain .withHeaders(…) to the return statement

Page 28: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

TAFFY DASHBOARD

Page 29: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

TAFFY DASHBOARD

▸ Dashboard allows to:

▸ execute requests (incl. Query Params, Headers and Basic Auth)

▸ inspect the response

▸ see a limited amount of documentation

▸ Should (usually) be switched off in production

▸ Default: caches API, needs explicit reloads

Page 30: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

CHANGING DATA - WHERE TO PUT POST?

▸ If the POST method was in Product.cfc - we’d have an issue.

▸ The URL route requires us to use /product/{productId}

▸ POST therefore has to life in ProductCollection.cfc

▸ PUT however (updating existing data) can happily go into Product.cfc

▸ URL matching: /product/search will match before /product/{productId}

Page 31: API management with Taffy and API Blueprint

TAFFY SETUP AND BASIC FLOW

GOOD PRACTICES FOR PUT AND POST

▸ If you created a record with POST:

▸ return “201 Created” with the created data

▸ potentially return x-inserted-id in the header

▸ If you updated a record with PUT:

▸ return “200 OK” with the updated data

▸ if the record to be updated didn’t exist, create it (see above)

Page 32: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

Page 33: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

BREAKING AWAY FROM THE CONVENTIONS

▸ Metadata:

▸ Annotate functions to be triggered by a specific verb: taffy_verb=“<VERB>”

▸ Necessary for extended verbs like OPTIONS, HEAD, PATCH. Optional for standard verbs.

▸ There’s more metadata in serialisers (later)

Page 34: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

BREAKING AWAY FROM THE CONVENTIONS

▸ Configuration: variables.framework struct in Taffy’s Application.cfc

▸ reloadKey/reloadPassword vs. reloadOnEveryRequest

▸ serializer/deserializer (later)

▸ disableDashboard

▸ jsonp/allowCrossDomain

▸ beanFactory - allows hooking into Coldspring, DI/1 etc…

Page 35: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

AUTHENTICATION AND SECURITY

▸ Firstly - what do you want to achieve?

▸ Requiring an API Token?

▸ Fine-grain security?

▸ oAuth

▸ HTTP Basic Auth

▸ SSL

Page 36: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

API TOKEN (I)

▸ Best place to start is onTaffyRequest(…)

▸ Return true to continue processing the request

▸ Return a representation object to abort the request

▸ Two common options for passing token:

▸ URI parameters and/or as part of the data

▸ In the header

Page 37: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

API TOKEN (II)

function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers, methodMetadata, matchedURI) {

if (not structKeyExists(arguments.requestArguments, "apiKey") || !Len(arguments.requestArguments["apiKey"])) { return noData().withStatus(401, "Unauthorized"); }

if (!arrayFind(application.validAPIKeys, arguments.requestArguments["apiKey"])) { return noData().withStatus(403, "Forbidden"); }

return true; }

Page 38: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

CUSTOM SERIALISERS (I)

▸ Default serialiser is JSON (CFML-server built-in)

▸ Extend from taffy.core.baseRepresentation

▸ Implement getAsX functions

▸ getAsXML, getAsJSON, getAsYML etc

Page 39: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

CUSTOM SERIALISERS (II)

component extends="taffy.core.baseSerializer" { variables.anythingToXML = application.anythingToXML; variables.jsonUtil = application.jsonUtil;

function getAsXML() output="false" taffy_mime="application/xml" taffy_default="false" { return variables.anythingToXML.toXml(variables.data); }

function getAsJSON() output="false" taffy_mime="application/json" taffy_default="true" { return variables.jsonUtil.serialize(variables.data); } }

Page 40: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

RATE LIMITS

▸ In a nutshell:

▸ manage access log (api key & time) in DB or key/value storage

▸ onTaffyRequest logs requests and check if limits are exceeded

▸ if not exceeded, process incoming request

▸ if exceeded, return status code “429 Too Many Requests”

Page 41: API management with Taffy and API Blueprint

ADVANCED TASKS AND CONFIG

API VERSIONING

▸ Multiple schools of thought:

▸ URI vs header

▸ Make sure you version from day 0!

▸ I tend to use /v1/product, /v2/product etc.

▸ Maps nicely on directory structures in /resources

▸ Update version when breaking compatibility

▸ Consider SemVer (Semantic Versioning) if your API is changing a lot

Page 42: API management with Taffy and API Blueprint

API BLUEPRINT AND OTHER TOOLS

https://www.flickr.com/photos/cardoso/2196726835/

Page 43: API management with Taffy and API Blueprint

API BLUEPRINT

WHAT IS IT?

▸ Description and specification language for web APIs (http://apiblueprint.org)

▸ From Apiary.io - but can be used independently from that

▸ Language spec: https://github.com/apiaryio/api-blueprint

▸ Offers platform-neutral documentation and additional tooling

▸ Aglio

▸ API-Mock

Page 44: API management with Taffy and API Blueprint

API BLUEPRINT

DOCUMENTATION WITH AGLIO

▸ Very simple markup language

▸ npm install -g aglio (https://www.npmjs.com/package/aglio)

▸ aglio -i documentation.md -o documentation.html

▸ MD structure:

▸ # Group

▸ ## Resource

▸ ### Action

Page 45: API management with Taffy and API Blueprint

API BLUEPRINT

MOCK SERVER WITH API-MOCK

▸ Creates a mock server for your API (https://github.com/localmed/api-mock)

▸ npm install -g api-mock

▸ api-mock ./documentation.md

▸ Defaults to port 3000

Page 46: API management with Taffy and API Blueprint

API BLUEPRINT

PAW

▸ REST API test and management tool on OS X

▸ Recording/Playback/Testing etc

▸ Has API Blueprint support through extensions

Page 47: API management with Taffy and API Blueprint

FINAL THOUGHTS

https://www.flickr.com/photos/brickset/16099265973/

Page 48: API management with Taffy and API Blueprint

FINAL THOUGHTS

WHAT DID WE COVER?

▸ Taffy - what is it?

▸ How to setup and the foundations

▸ Discussed some advanced topics in more detail

▸ High-level overview of API Blueprint

Page 49: API management with Taffy and API Blueprint

FINAL THOUGHTS

GET IN TOUCH

Kai Koenig

Email: [email protected]

Twitter: @AgentK