Forum Discussion

siriousje's avatar
siriousje
Regular Visitor
6 years ago

content negotiation and versioning

I am looking to document a REST API that uses content negotiation for versioning. In short, the application is made in Jersey 2 and already works. Some endpoints in the API are versioned using content negotiation (e.g., they all started out as 'produces: application/json' but newer endpoints under the same path can have an updated version (like 'produces: application/vnd.myapi.20190110+json') which can have both a different return type and a different set of arguments or payload body.

 

So, Jersey works fine with 

@Path("/endpoint")
@Produces("application/json")
@POST
A doV1(B b)

and

@Path("/endpoint")
@Produces("application/vnd.myapi.2+json")
@POST
X doV2(Y y, Z z)

It understands that both methods reside under /endpoint, but what it accepts as payload and what it returns is based on the Accept header.

I've been trying for a while now to make this visible in myapi.yaml but I can't seem be able to slot this level in a single document. As in, I can define different content types for an endpoint, but I can't define the version, like

paths: 
  /endpoint:
    post:
      "application/json":
        ...
      "application/vnd.myapi.2+json"
        ...

where I would be able to define different parameters/arguments and response types per content type

 

Any ideas how this can be solved? Like, do I need to create a new, complete document, for each change to any endpoint to reflect that the API has changed?

 

The idea here is that we keep resources at the same path though the payload of the resource itself may change with new versions and not all endpoints update at the same time (e.g., /endpoint could be at application/vnd.myapi.2+json while /somethingelse would be at application/vnd.myapi.5+json), allowing partners to slowly migrate endpoints over time

  • Hello!

     

    This is a great question, and an interesting one as well.

     

    As I'm sure you're aware, there are quite a few different ways to handle versioning, and content negotiation is indeed one of them.

     

    First thing, it depends on which version of the spec you use. In 2.0, there's pretty much no way of describing that. In 3.0, there is a way to do what you want - but only in part.

     

    Content negotiation applies to the payloads - either of the request, or the response. In the request, the consumer tells the producer what they're sending using the `Content-Type` header, and at the same time it asks the producer for a specific content reply using the `Accept` header. When the producer sends a reply, they'll use the `Content-Type` header in turn to describe what it is sending.

     

    So where's the catch? The `Accept` header is a request, but the the producer doesn't have to comply. If it doesn't know how to produce the requested media type, it's perfectly legal for it to return a different one. As such, there's no strong correlation between the request media type and the response media type.

     

    Furthermore, as mentioned above, the media type only impacts the content of the payload, not any other parameters. So in effect, there's nothing to make that conditional expectation that if you want a specific media type response (in your case a specific version), you'd have to send such and such query/header/path parameters.

     

    At the moment, the specification does not allow you to define dependencies between different request parameters and a specific response, for the same given path. There are discussions to enable something like that in future versions, but nothing concrete is available.