API Design



  • I'm in the process of writing an application for my new employer, and I'm trying to decide on how to design the API version/object scheme.

    Until now, the endpoints have all really only needed to reply with one kind of object. I'm now writing the "management" interface, and each endpoint makes sense to supply both sides of the application, but they have wildly different information needs. My issue is that there's no guarantee that today's "management" object might be tomorrow's "app" object ... or each side might need specific versions for each application (iOS, Android, Web) ... and that ends up looking like a lot of API sprawl to me.

    My solution was to use application/vnd.[stuff]+json Accepts headers with object versions in addition to endpoint versions ... but this still feels almost needlessly complex. Any experience on either or both approaches would be appreciated.


  • Notification Spam Recipient

    @rad131304 said in API Design:

    use application/vnd.[stuff]+json Accepts headers with object versions in addition to endpoint versions

    So your version endpoints could vary between vendors? Why not just tie the vendor to the route instead (i.e. vendorspecific.theapihost.com/api/version/whateverendpoint)?

    Then again, I don't design APIs, so who knows?



  • I'm not super qualified to give you a definitive answer. All my instincts, however, scream against using request headers to influence the resource you get. Stick to the api paths and don't mind the sprawl. If you make you api layer thin enough, it won't be a problem to maintain in the backend.


  • Discourse touched me in a no-no place

    @rad131304 said in API Design:

    they have wildly different information needs

    Are they really the same object then?

    I'm of the opinion that it's best to keep the API layer fairly direct and expose the underlying model relatively straight-forwardly. That stops you from going too mad. If you want to apply some sort of information selection profiles to objects, you might do that by adding query string parameters:

    /app/objects/123/splurgle?view=simple
    

    Like that, code can select the fields it wants in fairly sane patterns (and you can implement defaults when you feel like it) without disturbing that the actual path names the entity that you're talking about.

    The actual format that you retrieve (e.g., JSON or XML) ought to be done by content negotiation.


  • Winner of the 2016 Presidential Election

    @dkf said in API Design:

    The actual format that you retrieve (e.g., JSON or XML) ought to be done by content negotiation.

    A lot of frameworks just use a fake file extension for that. Works as well…


  • Discourse touched me in a no-no place

    @asdf said in API Design:

    A lot of frameworks just use a fake file extension for that.

    A lot of frameworks are stinking piles of mongoose vomit, at least from a REST design perspective.


  • FoxDev

    @dkf said in API Design:

    A lot of frameworks are stinking piles of mongoose vomit

    i've yet to find one that wasn't.

    i've found severals that have carefully hidden the piles away from casual view.

    i've even found a couple that have caged the piles behind triple security doors and chrome plated everything into the prettiest thing ever, but that pile of fetid mongoose vomit is there, waiting for you to wander far enough off the beaten and chrome plated path just far enough to step in it.


  • Discourse touched me in a no-no place

    @accalia I've been relatively happy with Apache CXF (modulo the fact that it's a Java framework). There are some tricky bits, but they're generally tricky because the thing in question is tricky, so I'm not upset over that. It can also let me push bytes directly when I need to, which makes moving even large amounts of data quite easy. It also doesn't try to take over your life; it knows its place and sticks to it.

    The only real downside is that the code is internally complicated, so you get bunker-busting stack traces. Yet you virtually never need to look under the hood.


  • FoxDev

    @dkf said in API Design:

    The only real downside is that the code is internally complicated, so you get bunker-busting stack traces. Yet you virtually never need to look under the hood.

    ah. one of the chromeplated ones.

    still it seems you are at least aware that the mongoose vomit is in there too.

    i think it's one of those things where if we actually left the mongoose vomit out things would stop working......



  • @accalia it's unavoidable in web development, that's why I avoid web development. browsers run on vomit



  • @dkf said in API Design:

    @rad131304 said in API Design:

    they have wildly different information needs

    Are they really the same object then?

    I'm of the opinion that it's best to keep the API layer fairly direct and expose the underlying model relatively straight-forwardly. That stops you from going too mad. If you want to apply some sort of information selection profiles to objects, you might do that by adding query string parameters:

    /app/objects/123/splurgle?view=simple
    

    Like that, code can select the fields it wants in fairly sane patterns (and you can implement defaults when you feel like it) without disturbing that the actual path names the entity that you're talking about.

    The actual format that you retrieve (e.g., JSON or XML) ought to be done by content negotiation.

    It's a bit different than just a "simple" vs "detailed" request - when I hit the endpoint from a management perspective, I only want the 5 or 6 items that I'm actually allowed to manage, but when I hit it from the app perspective, I need paged results in, say, batches of 20.

    I was being too complicated. Your approach seems like the sanest way to deal with the issue.


  • Fake News

    @rad131304 said in API Design:

    My solution was to use application/vnd.[stuff]+json Accepts headers with object versions in addition to endpoint versions ... but this still feels almost needlessly complex. Any experience on either or both approaches would be appreciated.

    I'm a fan of the Content-type header. One thing I use it for is when you have a resource and want to send two different "messages" to it to indicate a different state change, e.g. a "succeeded" and a "failed" message. Versioning works too. Similarly the Accepts header works well for simple negotiating like versions or XML vs. JSON.

    However, returning completely different information from GETting a resource with a different Accepts header seems like a smell. For that I'd use a different URI, maybe like dkf's example. Using headers on a POST gives me less trouble.

    The real trouble with this Content-type setup is that it's somewhat hard to get running some frameworks. ASP.NET MVC WebAPI needs custom attributes while Java's JAX-RS can do it without breaking a sweat (though anyone working with WebAPI will soon realize that it is very flexible but doesn't come with anything out of the box - MIME-multipart serialization, I'm looking at you). It might be worth investigating your platforms before you commit to a design.


    Of course, all of this is very hard to reason about without a real example. Giving examples of an "app" object versus a "management" object might help to understand your design.


Log in to reply