40
Crafting Evolvable API Responses

Prairie DevCon 2015 - Crafting Evolvable API Responses

Embed Size (px)

Citation preview

Page 1: Prairie DevCon 2015 - Crafting Evolvable API Responses

Crafting Evolvable API Responses

Page 2: Prairie DevCon 2015 - Crafting Evolvable API Responses

Who am I?

• Twitter: @darrel_miller• http://www.bizcoder.com/

Solve API Problems Fast

Page 3: Prairie DevCon 2015 - Crafting Evolvable API Responses

Our Journey Today

• Focus on API responses• Versioning is painful• Why we think we need it?• How can we avoid it?• What if you can’t?

Page 4: Prairie DevCon 2015 - Crafting Evolvable API Responses

Objects over the wire

Page 5: Prairie DevCon 2015 - Crafting Evolvable API Responses

We have been here before

• CORBA, DCOM• SOAP, WSDL• DTOs• JSON

Page 6: Prairie DevCon 2015 - Crafting Evolvable API Responses

The ASP.NET Web API Project Template

public class ValuesController : ApiController { // GET api/values public IEnumerable<string> Get() { return new string[] { "value1", "value2" };}

// GET api/values/5 public string Get(int id) { return "value"; }

// POST api/values public void Post([FromBody]string value) { }

// PUT api/values/5 public void Put(int id, [FromBody]string value) { }

// DELETE api/values/5 public void Delete(int id) { } }

Page 7: Prairie DevCon 2015 - Crafting Evolvable API Responses

The ASP.NET Web API Starter Tutorialpublic class ProductsController : ApiController { //…

public IEnumerable<Product> GetAllProducts() { return products; }

public IHttpActionResult GetProduct(int id) { var product = products.FirstOrDefault((p) => p.Id == id); if (product == null) { return NotFound(); } return Ok(product); } }

http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api

Page 8: Prairie DevCon 2015 - Crafting Evolvable API Responses

ServiceStack

public class ReqstarsService : Service { public List<Reqstar> Get(AllReqstars request) { return Db.Select<Reqstar>(); } }

Page 9: Prairie DevCon 2015 - Crafting Evolvable API Responses

NancyFX

public class SampleModule : Nancy.NancyModule{ public SampleModule() { Get["/"] = _ => "Hello World!"; }}

Page 10: Prairie DevCon 2015 - Crafting Evolvable API Responses

Python Flask

@app.route('/todo/api/v1.0/tasks', methods=['GET'])def get_tasks(): return jsonify({'tasks': tasks})

Page 11: Prairie DevCon 2015 - Crafting Evolvable API Responses

Rails

# Returns the resource from the created instance variable # @return [Object] def get_resource instance_variable_get("@#{resource_name}") end

Page 12: Prairie DevCon 2015 - Crafting Evolvable API Responses

But, what’s the problem?

Page 13: Prairie DevCon 2015 - Crafting Evolvable API Responses

Which objects to map?

• Domain objects • How do we hide content we don’t want to expose?• How do we create different views of data?• Changes to domain model cause changes to API

• DTOs• whole lot of busy work• Only indirect control over serialization process

Page 14: Prairie DevCon 2015 - Crafting Evolvable API Responses

Automatic Serialization

• All the properties• Missing values, unrecognized properties• Non standard data types: datetime, timespan• Does null mean unspecified, or explicitly null?• Empty collection or no collection• Capitalization• Links• Cycles• Changes to behavior in the frameworks• Security Risks

Page 15: Prairie DevCon 2015 - Crafting Evolvable API Responses

The whole app is useless because the API added a new preference

Page 16: Prairie DevCon 2015 - Crafting Evolvable API Responses

APIs Break because Serialization Libraries Change/Fix things

Page 17: Prairie DevCon 2015 - Crafting Evolvable API Responses

Take back control

Page 18: Prairie DevCon 2015 - Crafting Evolvable API Responses

Use a DOM to build your document

dynamic jspeaker = new JObject();jspeaker.name = speakerInfo.Name;jspeaker.bio = speakerInfo.Bio;

dynamic links = new JObject();

dynamic iconLink = new JObject();iconLink.href = speakerInfo.ImageUrl;links.icon = iconLink;

dynamic sessionsLink = new JObject();sessionsLink.href = SessionsLinkHelper.CreateLink(request, speakerInfo).Target;links[LinkHelper.GetLinkRelationTypeName<SessionsLink>()] = sessionsLink;

jspeaker["_links"] = links;return new DynamicHalContent(jspeaker);

Page 19: Prairie DevCon 2015 - Crafting Evolvable API Responses

What to do with your new found freedom?

Page 20: Prairie DevCon 2015 - Crafting Evolvable API Responses

Resources and Representations

/api/order/34

/api/order/35

/api/order/35/invoice.html

application/json

application/ld+json

/api/order/35/invoice.pdf

application/json

application/ld+json

application/pdf

text/html

/api/PrintQueue

/api/CurrentWeather

/api/LeaderBoard

Page 21: Prairie DevCon 2015 - Crafting Evolvable API Responses

Anatomy of an HTTP representation

200 OK HTTP/1.1

Server: Microsoft-HTTPAPI/2.0Content-Length: 44Content-Type: text/plainAcme-perf-database-cost: 120ms

The quick brown fox jumped over the lazy dog

Page 22: Prairie DevCon 2015 - Crafting Evolvable API Responses

Let’s build some payloads

Page 23: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"There is a hole in my bucket"

}

The smallest thing that is actionable

Page 24: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"There is a hole in my bucket","steps_to_reproduce" : "Pour water in bucket. Lift bucket off ground.

Look for water dripping", "date_reported": "2012-04-21T18:25:43-05:00"

}

Just enough data to solve the problem

Page 25: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big","application_name" : "Wordament","application_version" : "1.2.0.2392","environment_osversion" : "NT4.0","environment_memory_free" : "100 MB","environment_diskspace_free" : "1.5 GB", "reported_by_user" : “Bob Bing”

}

Why do they need that data?

Page 26: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big","application_name" : "Wordament","application_version" : "1.2.0.2392","environment" : { "osversion" : "NT4.0",

"memory_free" : "100 MB","diskspace_free" : "1.5 GB"

}}

Attribute Groups

Page 27: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big","history" : [

{"status" : "reported", "date" :"2014-02-01"},{"status" : "triaged", "date" :"2014-02-04"},{"status" : "assigned", "date" :"2014-02-12"},{"status" : "resolved", "date" :"2014-02-19"},

]}

Attribute Groups for multiple instances

Page 28: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big","reported_by_user" : { "name" : "Bob Bing",

"email" : "[email protected]", "twitter" : "@bobbing", "date_hired" : "2001-01-21"

}}

Attribute Groups for related data

Page 29: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big","reported_by_user_url" : "http://api.acme.com/users/75"

}

Linking related data

Page 30: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big",“links" : [

{ "href" :"http://api.acme.com/users/75", "rel": "reportedbyuser" },{ "href" :"http://api.acme.com/users/24", "rel": "assignedtouser" }

] }

Multiple Links

Page 31: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"issues" : [

{"description" :"There is a hole in my bucket"

}]

}

Beware of structural changes{

"issue" : {

"description" :"There is a hole in my bucket"}

}

Item in a Array

Item as a Property

Page 32: Prairie DevCon 2015 - Crafting Evolvable API Responses

Naming Your Conventions

• Empty arrays• Nulls• Single item as array or object• Camel case, snake case• Vocabulary• Protocols

Page 33: Prairie DevCon 2015 - Crafting Evolvable API Responses

Media Type Scope

application/jsonapplication/vnd.acme.issue+json

application/issue+json

Too broadToo narrowIdeal

application/hal+json Partialapplication/vnd.acmeapi+json Compromise

Vocabularies : Profiles, Namespaces, Schemas, Ontologies

Page 34: Prairie DevCon 2015 - Crafting Evolvable API Responses

Learn from others

application/hal+jsonapplication/ld+jsonapplication/vnd.mason+jsonapplication/vnd.siren+jsonapplication/vnd.amundsen-uber+json

application/vnd.github.v3+jsonapplication/vnd.heroku+json

application/vnd.api+jsonapplication/atom+xmlapplication/voicexml+xmlapplication/vnd.collection+jsonapplication/activity+jsonapplication/http-problemapplication/json-home

Generic Hypermedia API Specific Task Specific

Page 35: Prairie DevCon 2015 - Crafting Evolvable API Responses

{"description" :"The font is too big","_embedded" : {

"reportedByUser" : { "name" : "Bob Bing",

"email" : "[email protected]","_links" : { "self" : {"href" :"http://api.acme.com/users/75"}}

}

}

Meet application/hal+json

Page 36: Prairie DevCon 2015 - Crafting Evolvable API Responses

{ "collection": { "links": [], "items": [ { "data": [ { "name": "Title", "value": "\r\n\t\t\tLearning from Noda Time: a case study in API design and open source (good, bad and ugly)\r\n\t\t“ }, { "name": "Timeslot", "value": "04 December 2013 16:20 - 17:20“ }, { "name": "Speaker", "value": "Jon Skeet“ } ], "links": [ { "href": "http://conference.hypermediaapi.com/speaker/6", "rel": "http://tavis.net/rels/speaker" }, { "href": "http://conference.hypermediaapi.com/session/133/topics", "rel": "http://tavis.net/rels/topics" } ], "href": "http://conference.hypermediaapi.com/session/133" } ], "query": [], "template": { "data": [] }, "version": "1.0" }}

Meet application/vnd.collection+json

Page 37: Prairie DevCon 2015 - Crafting Evolvable API Responses

Version as a Last Resort

• Version the payload• HTML DOCTYPE, Collection+Json

• Version the resource• /api/documents/invoice.v2/758

• Version the media type• application/vnd.github.v3+json

• Version the API• /api/v2/documents/invoice/758

Page 38: Prairie DevCon 2015 - Crafting Evolvable API Responses

Wrap up

• Understand the limitations of “objects over the wire”• Consider taking back control of your representations• Think in terms of messages, instead of objects• Build software that is designed to survive change• Believe that versioning is an admission of failure

Page 39: Prairie DevCon 2015 - Crafting Evolvable API Responses
Page 40: Prairie DevCon 2015 - Crafting Evolvable API Responses

Image Credits

• Package - https://flic.kr/p/3mrNyn• Freedom - https://flic.kr/p/4vwRDw• Treasure map - https://flic.kr/p/7jDJwi• Handshake - https://flic.kr/p/nbAu8Y• Telephone - https://flic.kr/p/7Q8bMd• Blindfolded Typing - https://flic.kr/p/53Q3JE• Magic Trick - https://flic.kr/p/7T8zk5• Donut machine - https://flic.kr/p/anncxf• GT-R Steering Wheel - https://flic.kr/p/fDUSDk• Anatomy - https://flic.kr/p/6bfUZn• Shapes - https://flic.kr/p/3aKUAq• Payloaders - https://flic.kr/p/dTU9sN• Birds on a Wire - https://flic.kr/p/4YdfK