Upgrade to Pro — share decks privately, control downloads, hide ads and more …

APIs for the REST of Us

APIs for the REST of Us

How to use APIs to reuse, refactor and rewrite legacy applications.

Davey Shafik

July 30, 2012
Tweet

More Decks by Davey Shafik

Other Decks in Programming

Transcript

  1. •Engineer at Engine Yard for Orchestra.io PHP Platform as a

    Service (PaaS) •Author of Zend PHP 5 Certification Study Guide, Sitepoints PHP Anthology: 101 Essential Tips, Tricks & Hacks & PHP Master: Write Cutting Edge Code •A contributor to Zend Framework, phpdoc, FRAPI and PHP internals •@dshafik Davey Shafik
  2. Reuse (What you want to) Refactor (What you can) Rewrite

    (What you need to) The 3 R’s of Software Development
  3. FRAPI • Mature: Over 2 years old • Current Release:

    0.1.1 (7/2/2012) • Upcoming Release: 0.2.0 (Hustle) • http://getfrapi.com • http://github.com/frapi/frapi
  4. Super Fast Router • Routes custom URLs to Actions •

    Custom URL parameters: • /a/:collection/:resource_id • Actions represent Collections and Resources • Actions support 5 HTTP methods: • GET,1POST,1PUT,1DELETE,1HEAD • Coming Soon: PATCH
  5. Content Negotiation • Correctly respects q-values • Request: Accept:1text/json;q=0.5,1application/json •

    Response: ContentGType:1application/json • Allows custom mimetypes • Map to any output type • Allows params, automatically parsed • application/vnd.github.:version.:param+:format • Responds with the correct mimetype • Supports mimetype params • application/json;version=:version;param=:param • Undefined params also handled (e.g. charset=utf-8)
  6. Abstracts Input • Transparently allows access to: • GET/POST args

    • HTTP body automatically parsed into easily accessible data structures • Content@Type:Capplication/json,Capplication/xml,Ctext/json,Ctext/xml • $thisG>getParam('name'); • Easy access to mimetype params and... mimetype params • $thisG>getAcceptParam('name');
  7. Abstracts Output • Actions always return PHP data structures (either

    a Frapi_Response object, or an array) • Frapi serializes to requested format on output • Out of the box support for: • JSON, JSON-P, XML, PHP serialized, PHP printr, CLI (plain text) and HTML (requires custom templates) • You can disable any you don’t want to use! • Create custom outputs (such as CSV or Adobe AMF)
  8. Admin Interface • Simple interface for managing actions, mimetypes, output

    types and other configuration • Allows for documenting your API • Supports markdown with syntax highlighting • More documentation-related features coming • Entire interface currently being overhauled • User Interface and User Experience enhancements (UI/UX++) Best Practices: Do not deploy admin to production
  9. Versioning • Versioned URLs are bad • Mimetypes are better:

    • Accept:Capplication/vnd.myvendor.:version+:type • MimetypeCParamsCareCevenCbetter: • Accept:Capplication/json;version=:version • Either way, in FRAPI: • $thisG>getAcceptParam('version');
  10. Coming Soon... • Hustle (0.2.0) • Build System (PEAR or

    Composer) • New Admin Interface • Repository reorganization for faster deploys • Bump (0.3.0) • Better tools for: • HATEOAS/Hypermedia • Documentation • Automated Mocks (a la Apiary.io) • Tester
  11. TurnAPI.com • Beta • Slow moving on features • Feature

    set is decent (versioned documentation, etc) • Markdown syntax with extensions • Free while in beta • Has been in beta for a long... long time. • Open signup
  12. Apiary.io • Beta • Feature set is excellent • Automatically

    creates mock API from documentation to write clients against • Debug console • Markdown syntax with extensions • Free while in beta • Closed signup • Lots of success tweeting them for invite codes: @apiaryio
  13. Reuse •Models •Business Logic •Libraries •Data sources •Almost anything but

    your views Pros: Quickly piggy-back off your current code. Get an API into production now. Cons: Will likely lead to REST-like (or RESTy/RESTful) APIs that are RPC in reality.
  14. Reuse: Best Practices • Design your API first • APIs

    are like URIs: They should be permanent • Don’t let bad legacy decision drive new development Do: Pretend your API is a re-write. Create the best API you can for your application. Then implement it using legacy code any way you can. Even if it sucks. Don’t: Make compromises based on old code. Instead, plan to later refactor the old code.
  15. Refactor • Two types of refactoring: • Refactor your app

    to use your APIs instead of legacy code. Then make the APIs better by: • Refactor the underlying implementation of your APIs to migrate away from legacy code Do: Test. Unit Tests and Integration Tests. Frisby.js is a great BDD integration testing system. Don’t: change your API! If you designed it right during the Reuse phase, this should be easy!
  16. Rewrite • Rewriting should be your last step • By

    the time you get here, you should have full testing in place • You should have separation of concerns via SOA, so you can rewrite piecemeal Do: Discard any and all bad legacy code. Treat it as a clean slate. Don’t: Rewrite just to rewrite! Legacy code isn’t inherently bad code. Focus on new features. Build them as services!
  17. Step-By-Step 1. Design your API 2. Build by Reusing legacy

    code (Do whatever it takes!) 3. Release 4. Refactor Application to use API instead of Legacy code 5. Refactor (or Rewrite) API to not use Legacy code. 6. Reuse API in Legacy application Rewrite (or Reuse in an entirely new application!)
  18. Testing • Integration Testing • Test at a much higher

    level than Unit Testing • Ensures your API conforms to your design • Can be used for BDD — Behavior Driver Development • Use the excellent Frisby.js by Vance Lucas: http://frisbyjs.com
  19. Writing Tests varCfrisbyC=Crequire('frisby'); frisby.create('TestCGETC/collection') CCCC.get('http://api.frapi/collection') CCCC.auth('test',C'34c285b25ac62f9472265d1e41f8a77f5d2382f6') CCCCCCCC.expectStatus(200) CCCCCCCC.expectHeaderContains('content@type',C'application/json') CCCCCCCC.expectJSON({C CCCCCCCCCCCCmeta:C{C

    CCCCCCCCCCCCCCCCtotal:C'N', CCCCCCCCCCCCCCCCdesc:C'TheCtotalCshouldCbeCtheCactiveCresourcesC containedCinCaCcollection/bucket.' CCCCCCCCCCCC}, CCCCCCCCCCCCresources:C{C CCCCCCCCCCCCCCCCres1:C{Chref:C'/collection/res1',Cname:C'res1'C}, CCCCCCCCCCCCCCCCres2:C{Chref:C'/collection/res2',Cname:C'res2'C}, CCCCCCCCCCCCCCCCres3:C{Chref:C'/collection/res3',Cname:C'res3'C} CCCCCCCCCCCC} CCCCCCCC}) .toss()
  20. Making Requests • Set Headers • addHeader("Content@Type",C"application/json") • removeHeader("Content@Type") •

    Basic HTTP Authentication • auth(username,Cpassword) • Doesn’t support Digest or other types of auth • Call any HTTP method • .get(), .post(), .put(), .delete(), .head() • No PATCH support
  21. Verifying Responses • Set response expectations • expectHeader("Content@Type",C"application/json"), expectHeaderContains("Content@Type",C"json") •

    expectBodyContains("sometext") • expectJSON({foo:C"bar"}),CexpectJSON(foo,C"bar") • expectJSONTypes({foo:CString}),CexpectJSONTypes("foo",CC String) • expectJSONLength(1)
  22. Jasmine Assertions • toBe(): Comparison using === • toEqual(): Comparison

    using == • toMatch(): Comparison using RegExp • toBeDefined(), toBeUndefined(): Checks against undefined • toBeNull(): Checks for ===Cnull • toBeTruthy(), toBeFalsy(): Checks non-boolean truthfulness • toContain(): Checks for a value inside an Array • toBeGreaterThan()/toBeLessThan(): Checks < and > • toBeCloseTo(): Checks with a specified level of precision • toThrow(): Checks that a function throws an exception • Precede with not to invert, e.g.Cexpect(expr).not.toBeNull()