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

How we build APIs @ Huddle

yiannis
September 17, 2014
160

How we build APIs @ Huddle

yiannis

September 17, 2014
Tweet

Transcript

  1. View Slide

  2. Yiannis Triantafyllopoulos
    How we build APIs @ Huddle

    View Slide

  3. /agenda
    • Design
    • Hypermedia
    • Media types
    • Versioning
    • Testing
    • APIs in overall architecture

    View Slide

  4. /design
    • Big design up front is dumb, no design up front is dumber
    • Documentation-driven design
    • Endpoints, verbs, status codes and examples
    • Feedback from everyone
    • Consult and validate during code review, arch review and manual
    testing
    • Use Wiki or Markdown because why not?
    • API != CRUD
    • Follow use cases, not database tables
    • Eat your own dog food

    View Slide

  5. /hypermedia
    • One and only one entry point
    • Follow links from there
    • Nice URIs are cute but conceptually treat them as
    opaque strings
    • Avoid URI templates
    • /resource/{resourceId}/subresource/{subresourceId}

    View Slide

  6. /hypermedia-entry-point
    GET /entry
    200 OK
    {
    „links‟:[
    {„rel‟:‟self‟,„href‟:‟…/users/42‟},
    {„rel‟:‟avatar‟,„type‟:‟image/jpg‟, „href‟:‟…‟
    {„rel‟:‟notifications-received‟,‟href‟:‟…‟}
    „membership‟:{

    }
    }

    View Slide

  7. /media-types
    • Contract between client and server on how to process
    the response
    • No application specifics here
    • Choice of generic vs. custom (link)
    • HAL
    • JSON-LD
    • JSON API
    • Collection+JSON

    View Slide

  8. /media-types
    • application/vnd.huddle.data+json
    • links
    • Represents an action that can be performed against a resource
    • actors
    • Represents a user who has performed an action against a
    resource e.g. owner, manager, assignee
    • Opt for a generic one if you can
    • If you have to define your own, make sure you separate it
    from your domain!

    View Slide

  9. /media-types
    • “edit” link relation
    Example
    GET /resource/123
    200 OK { „self‟:‟/resources/123‟, „edit‟:‟/resources/123/edit‟ }
    GET /resources/123/edit
    200 OK { „name‟:‟foo‟ }
    PUT /resources/123/edit
    { „name‟:‟bar‟ }
    204 No Content

    View Slide

  10. /versioning
    • URI
    • GET /api/v2/resources/123
    • URIs identify resources and therefore should never change
    • Headers
    • Custom request header
    • X-Api-Version
    • Custom media type
    • application/vnd.github.v3+json
    • Custom media type per resource
    • application/vnd.github.user.v3+json
    • Clients need to set the correct headers for every call

    View Slide

  11. /versioning
    • Adding new links or resources doesn‟t break existing
    clients
    • Removing or renaming links or resources breaks
    existing clients
    • In practice though that doesn‟t happen very frequently
    • Make breaking changes early on in development so
    that you don‟t have to make them in production

    View Slide

  12. /testing
    • Unit tests
    • Handler level
    • C# using MSpec
    • Acceptance tests
    • Outside-in, mostly happy path
    • Python using requests, contexts

    View Slide

  13. /unit-test
    public class UserExists {
    Establish context => {
    user = new UserBuilder().WithName(“bob”).Persist();
    handler = new UserHandler(new SqlLiteRepository(), new Mock());
    };
    Because of => {
    response = handler.Get(user.Id);
    };
    It has_successful_response => response.StatusCode.ShouldEqual(200);
    It has_user_resource => response.View.Name.ShouldEqual(“bob”);
    }

    View Slide

  14. /acceptance-test
    @context
    class WhenGettingAUser(PersistedResource):
    def given_a_user(self):
    self.name = „bob‟
    self.user = self.persistUser(name=self.name)
    def when_getting_the_user(self):
    self.retrievedUser = self.api.getResource(self.user.selfUri, User.fromJson)
    def then_the_user_is_returned(self):
    assert self.retrievedUser.name == self.name

    View Slide

  15. /architecture
    • Hexagonal architecture (Alistair Cockburn)
    • API as a port
    • Framework as an adapter
    • CQRS
    • ViewBuilders and Queries for reads
    • Commands for everything else

    View Slide

  16. /ports-and-adapters

    View Slide

  17. /read-side
    readonly viewBuilder;
    readonly userQuery;
    public OperationalResult Get(int userId){
    var viewModel = userQuery.Execute(userId);
    if(viewModel == null){
    return new OperationResult.NotFound();
    }
    var userView = viewBuilder.Render(viewModel);
    return new OperationResult.OK(){ Resource = userView };
    }

    View Slide

  18. /write-side
    readonly commandDispatcher;
    public OperationalResult Delete(int userId){
    var deleteCommand = new DeleteUserCommand(){
    UserId = userId;
    };
    commandDispatcher.Send(deleteCommand);
    return new OperationResult.NoContent();
    }

    View Slide

  19. /the-end
    • APIs are use cases, design them for clients
    • Single entry point, follow links from there
    • Avoid versioning if you can
    • Test your port, not your stack

    View Slide

  20. /thank-you
    • We‟re hiring!
    • https://talentcommunity.huddle.com/careers

    View Slide

  21. View Slide