# MIME-based versioning via Accept header

**URL:** <https://softwaremill.community/t/mime-based-versioning-via-accept-header/116>\
**Category:** tapir\
**Created:** [February 5, 2023, 5:52pm UTC](https://softwaremill.community/t/mime-based-versioning-via-accept-header/116 "2023-02-05T17:52:27Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Brano](https://avatars.discourse-cdn.com/v4/letter/b/c68b51/32.png) [@Brano](https://softwaremill.community/u/Brano)\
**Post date:** [February 5, 2023, 5:52pm UTC](https://softwaremill.community/t/mime-based-versioning-via-accept-header/116/1 "2023-02-05T17:52:27Z")

</div>

Hey there!

Firstly, let me thank you for the great library you have created.

I’m currently trying to make MIME-based versioning (via accept header) work in Tapir,

i.e. I would like to have an endpoint accepting 2 (or more) custom media types (e.g. `application/resource-v1+json`, `application/resource-v2+json`) and based on the accept header of the request answering a JSON representing resource of the desired version.

I can make the endpoint accept one custom version (`application/resource-v1+json`) – with custom codecs for the new media type.

Although, as soon as I define multiple media types in accept header of the endpoint, e.g.:

`.in(header(Header.accept(MediaType("application", "resource-v1+json"), MediaType("application", "resource-v2+json"))))`

The behavior is as follows. Requests with accept header:

- `application/resource-v1+json` - yield 400 Bad request -

> Invalid value for: header Accept: application/resource-v1+json, application/resource-v2+json (value mismatch)

- `application/resource-v2+json` - yield 400 Bad request -

> Invalid value for: header Accept: application/resource-v1+json, application/resource-v2+json (value mismatch)

- `application/resource-v1+json, application/resource-v2+json` - yield 200 OK – which, obviously, is not the thing I would like to do.

I’m of undestanding that the endpoint should accept requests with any of the two media types in their accept header.

How can I make the MIME-based versioning work?

Any help is greatly appreciated.

---

<div class="post-metadata">

**Author:** ![adamw](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/adamw/32/28_2.png) [@adamw](https://softwaremill.community/u/adamw)\
**Post date:** [February 6, 2023, 1:23pm UTC](https://softwaremill.community/t/mime-based-versioning-via-accept-header/116/2 "2023-02-06T13:23:55Z")

</div>

I think there are two solutions to your problem:

1. a high-level one, using [`oneOf` inputs](https://tapir.softwaremill.com/en/latest/endpoint/oneof.html#oneof-outputs). Each variant’s body can have a different content type, and this should be used by tapir to implement content negotiation

2. a low-level one, by manually checking the `Accept` header. In your current code, you require that as part of the request, there’s an `Accept` header with the value _equal_ to `application/resource-v1+json, application/resource-v2+json` - that’s what the fixed-header input does. And that’s also the behavior you are observing. Instead, you want to check that the header matches _one_ of these values. The following input should do the trick:

```auto
  header[MediaType](HeaderNames.Accept).mapDecode { headerValue =>
    if (headerValue == MediaType("application", "resource-v1+json") || headerValue == MediaType("application", "resource-v2+json"))
      DecodeResult.Value(headerValue)
    else DecodeResult.Mismatch("resource-v1+json or resource-v2+json", headerValue.toString)
  }(identity)

```

This input checks that the value is in the allowed range, and reports a mismatch otherwise. In fact, I think adding an enumeration validator will be even better, as you should see the possibilities in the documentation:

```auto
  header[MediaType](HeaderNames.Accept).validate(
    Validator.enumeration(
      List(MediaType("application", "resource-v1+json"), MediaType("application", "resource-v2+json")),
      (v: MediaType) => Some(v.toString)
    )
  )

```

However, if the bodies are different, to have separate documentation for both, you’ll need to use `oneOf`.
