Crafting Evolvable Api Responses

  • View
    7.016

  • Download
    0

  • Category

    Software

Preview:

DESCRIPTION

Create evolvable responses to avoid versioning your Web API for as long as possible.

Citation preview

Crafting Evolvable API Responses

Who am I?

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

Journey

• Versioning sucks, let’s just not do it• Why we think we need it?• How can we avoid it?• Example time

Objects over the wire

We have been here before

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

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) { } }

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

ServiceStack

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

NancyFX

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

Python Flask

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

Rails

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

But, what’s the problem?

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

Automatic Serialization

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

Take back control

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);

What to do with your new found freedom?

Anatomy of an HTTP representation

• Status line• Headers• Body

Headers

• Performance• Timing • Naming• Content• Compression

Let’s build some payloads

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

}

The smallest thing that is actionable

{"issue" : {

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

}

Define a vocabulary for things of interest

application/json application/vnd.myapirespones+json application/vnd.issue+json

{"issues" : [

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

}]

}

Beware of structural changes

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

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

}

Just enough data to solve the problem

{"description" :"The font is too big","applicationName" : "Wordament","applicationVersion" : "1.2.0.2392","environment_OSVersion" : "NT4.0","environment_MemoryFree" : "10MB","environment_DiskSpaceFree" : "100TB",

}

Why do they need that data?

{"description" :"The font is too big","applicationName" : "Wordament","applicationVersion" : "1.2.0.2392","environment" : { "osVersion" : "NT4.0",

"memoryFree" : "10MB","diskSpaceFree" : "100TB"

}}

Attribute Groups

{"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

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

"email" : "bob@acme.com", "twitter" : "@bobbing", "dateHired" : "2001-01-21"

}}

Attribute Groups for related data

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

}

Linking related data

{"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

Learn from others

application/hal+json

application/vnd.collection+json

application/vnd.siren+json

application/vnd.github.v3+json

application/vnd.heroku+json

application/json-ld

application/json-home

application/http-problem

application/activity+json

application/vnd.mason+json

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

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

"email" : "bob@acme.com","_links" : { "self" : {"href" :"http://api.acme.com/users/75"}}

}

}

Meet application/hal+json

{ "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

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

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

Recommended