GET /cfml
A Guide to Writing API Wrappers
Matthew Clemente




APIs are awesome!
Superpowered Magic Legos



Everything has an API


Everything has an API
- Payments
- Search
- AI
- Physical Mail
- Maps/Geocoding
- APIs
- E-Commerce
- IoT
- SMS/Voice
- Accounting
- NLP/Text Analysis
- Books
- Calendars
- Cloud Storage
- Validation
- Dictionaries
- Weather
- Ticketing
- Music
- CI/CD
- Finance
- Beer
- Star Wars
- Law/Government
- Games and Comics
- Cryptocurrency
- Animals
- Machine Learning
- News
- Image Recognition
- Infrastructure
- File Sharing
- Ron Swanson
- Art and Design






What are we talking about?
Don’t Reinvent the Wheel

How do I get started?
🤔
Step #1
Don't Write Anything!

How I Got Here







Every API
is different
All APIs are the same
REST
REpresentational State Transfer
HTTP
HyperText Transfer Protocol
REST is CRUD over HTTP. Some exceptions are allowed, as long as they don’t become the rule.

Adam Tuttle, REST Assured: A Pragmatic Approach to API Design, 2019


The Cat API
Manual Approach

API Wrapper Approach


Don’t Reinvent the Wheel
hyper
A CFML HTTP Builder
box install hyper
- Fluent syntax to easily build requests
- Built to solve SDK-related frustration
- Not ColdBox specific
- By module-wizard @elpete
api-wrapper-template
A CommandBox tool for scaffolding CFML API Clients
box install api-wrapper-template
Getting Started
- Read the documentation
- Authentication
- Testing Endpoints
- Limitations, Format, etc.
- Review other client libraries
The Cat API Wrapper
apiWrapper scaffold --wizard
apiName* | Name of the API this library will wrap, i.e. Stripe |
apiEndpointUrl* | Base endpoint URL for API calls, i.e. https://api.stripe.com/v1 |
apiAuthentication | Type of authentication used [None, Basic, Apikey, Other] |
apiDocUrl | URL of the API documentation homepage |
name | Name for the wrapper [i.e. StripeCFC] |
description | A short description of the wrapper. |
author | Name of the author of the wrapper. |
package | Create a box.json - for use as a Forgebox package (yes/no) |
The Cat API Wrapper
apiwrapper scaffold
apiName = "The Cat API"
apiEndpointUrl = "https://api.thecatapi.com/v1"
apiAuthentication = "apikey"
apiDocUrl = "https://docs.thecatapi.com/"
name = "CFCat"
description = "Some people are dog people. Others are
cat people. This wrapper will provide
the latter with cats on demand."
package = "true"
Directory Structure


Building Your Requests
Making the Request

Naming Your Methods
- Model on API documentation
- Look to official clients
- CRUD pattern
- Be Consistent
Make sure there's a method to your madness
CRUD | HTTP Verb | Method |
---|---|---|
Create | POST |
createItem(data) |
Read | GET |
getItem(id) listItems() |
Update | PUT/PATCH |
updateItem(id) |
Delete | DELETE |
deleteItem(id) |
Naming Conventions
Be Consistent

catapi = new cfcat.catapi();
catGif = catapi.getRandomGif().data;
writeOutput( '<img src="#catGif.url#">' );
HTTP Response > Data

Make Debugging Easy

RequestBin / Hookbin / Mockbin
Being able to analyze your HTTP requests is helpful when debugging.

catapi = new cfcat.catapi( includeRaw = true );
data = {
"size": "full",
"breed_id": "raga",
"limit": 1
};
writeDump( catapi.search( data ) );
Authentication
Authentication typically involves providing credentials via Headers
No Authentication | This is easy! You don't need to do anything! |
Basic Authentication | Base64 encoded username:password in Authorization header |
API Key Authentication | Unique identifiers passed via headers (less frequently, via query params or the body) |
Open Authorization (OAuth) | Access Token used, following authentication and permission |
Authentication
How do we handle it in our CFML API clients?
No Authentication | This is easy! You don't need to do anything! |
Basic Authentication | cfhttp username and password attributes |
API Key Authentication |
cfhttpparam( type="header", name="api-key", value=value ); |
Open Authorization (OAuth) | https://github.com/coldfumonkeh/oauth2 |
Environment Variables
Keeping credentials in environment variables
provides practical and security benefits
Environment Variables
// Get access to the system class.
system = createObject( "java", "java.lang.System" );
// Get a specific environment variable
value1 = system.getenv( 'key' );
// Get a specific system property
value2 = system.getProperty( 'key' )
// Get a struct of env vars or system props
environment = system.getenv();
properties = system.getProperties();
Environment Variables
Handled automatically in api-wrapper-template

Aylien Text Analysis API
Aylien Text Analysis API
Aylien Text Analysis API
apiwrapper scaffold
apiName = "Aylien"
apiEndpointUrl = "https://api.aylien.com/api/v1"
apiAuthentication = "apikey"
apiDocUrl = "http://docs.aylien.com/textapi/"
name = "ayliencfc"
description = "Access Aylien's Natural Language
Processing (NLP) API to extract meaning
and insight from textual content."
package = "true"
Handling API Keys


Handling API Keys


Making a Request
URL to Analyze
Aylien Entity Analysis

aylien = new aylien.aylien();
entities = aylien.entities( url = 'http://bit.ly/cfsummit2019' ).data;
writeDump( entities );
Aylien Response Headers

aylien = new aylien.aylien();
entities = aylien.entities( url = 'http://bit.ly/cfsummit2019' );
writeDump( entities );

Shouldn't We Validate?

No...


Don’t Reinvent the Wheel
Leave Validation to the API

aylien = new aylien.aylien();
entities = aylien.entities( url = 'I am not a URL!' );
writeDump( entities );
Your Users = Developers
Make their lives easier by identifying pain points.

entities = aylien.entities( 'http://bit.ly/cfsummit2019' ).data;


Developer-focused Refactor #1

Developer-focused Refactor #2
Lob API for Printables
Lob API for Printables
apiwrapper scaffold
apiName = "Lob"
apiEndpointUrl = "https://api.lob.com/v1"
apiAuthentication = "basic"
apiDocUrl = "https://lob.com/docs"
name = "lobcfc"
description = "Wrap the Lob API to verify
addresses and send physical
mail programmatically."
package = "true"
Lob API for Printables
Basic Auth
& Test Endpoints




Basic Auth
& Test Endpoints
Lob Test Endpoint
lob = new lob.lob( forceTestMode = true );
//etc etc
result = lob.createPostcard( postcard );

Getting Into Arguments

Losing Arguments


Defusing Arguments

- Fast and easy to implement
- Preferable to listing arguments
- Puts burden on developer
- Data not reusable
Pass all parameters as a struct
How To Win Arguments

Helper components with fluent interfaces
- More initial work
- Better developer experience
- Consistent data produced
Helper Properties

/helpers/address.cfc
Chaining Methods
/helpers/address.cfc




Something is Wrong

Document Your Wrapper
(Because don't you prefer well documented repositories)
- Docs ≠ afterthought
- Add a method => add it to the docs
- Link to official documentation
- Quick start guide/example
- Describe the install procedure
- List the methods you provide
- Model a repo you admire
🧐📖
Share Your API Wrappers!





Don’t Reinvent the Wheel
GET /cfml
A Guide to Writing API Wrappers
Matthew Clemente



GET /cfml - A Guide to Writing API Wrappers
By mjclemente
GET /cfml - A Guide to Writing API Wrappers
Presentation for Adobe ColdFusion Summit 2019
- 4,238