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
  • Email
  • 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.

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