# What is a recommended API versioning in Tapir?

**URL:** <https://softwaremill.community/t/what-is-a-recommended-api-versioning-in-tapir/90>\
**Category:** tapir\
**Created:** [January 18, 2023, 8:25am UTC](https://softwaremill.community/t/what-is-a-recommended-api-versioning-in-tapir/90 "2023-01-18T08:25:15Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![liosedhel](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/liosedhel/32/60_2.png) [@liosedhel](https://softwaremill.community/u/liosedhel)\
**Post date:** [January 18, 2023, 8:25am UTC](https://softwaremill.community/t/what-is-a-recommended-api-versioning-in-tapir/90/1 "2023-01-18T08:25:15Z")

</div>

Hi!

I have a couple of questions regarding supported API versioning ways in Tapir.

1. Is there any way in tapir to version endpoints through the request body field?  
Let’s say I have such a request:  
Request version 1: `{data: String, version: Int = 1}`  
Request version 2: `{data: [String], version: Int = 2}`  
So the two requests are not backwards compatible, yet can be handled through the same HTTP endpoint route. Is there any convenient way to express such versioning in Tapir?  
To be honest, I don’t even know how this should be displayed with OpenAPI documentation - is this approach just “wrong”?

2. Can Tapir support versioning through the custom header? (similarly to content negotiation, I assume)

3. And finally - what is a recommended way of versioning API. I would assume that the URI path parameters, as it works out of the box with Tapir, can be documented nicely (just a different endpoint) and is the most flexible approach (as you can also change other path parameters in the next API versions).

Any other thoughts or considerations? 🙂

By the way, maybe a documentation page on API versioning would be a nice addition to Tapir docs (as I couldn’t find anything in this regard) 🙂

Best regards,  
Krzysztof

---

<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:** [January 18, 2023, 11:26am UTC](https://softwaremill.community/t/what-is-a-recommended-api-versioning-in-tapir/90/2 "2023-01-18T11:26:50Z")

</div>

I think OpenAPI can provide some guidance here. In OpenAPI, an endpoint is uniquely identified by the method + path template combination. So if we’d like to have both `v1` and `v2` endpoints in the same documentation page, a different path is required. By the way, endpoints for which method+paths overlap can be detected in a test using the [`EndpointVerifier`](https://tapir.softwaremill.com/en/latest/testing.html#shadowed-endpoints). Using `endpoint.tag`, endpoints can be grouped into categories (e.g. corresponding to the version).

Alternatively, versioning can also be performed using a query parameter or a header value. In this situation, you’ll probably want to deploy two documentation sites, one for `v1`, another one for `v2`. You can still have a single list of `ServerEndpoint`s to deploy, and partition them using an attribute / checking if an endpoint contains an input, to pass the proper endpoints the OpenAPI interpreter (you’d call the interpreter once for each version).

Differentiating the endpoints can be done using e.g. a fixed header: `.in(header("API-Version", "v1"))`. This way, only requests where the value of the header matches the given one will be allowed. If you need a default (fall-back) endpoint, e.g. if we assume that `v2` is the default version is it’s not provided explicitly, simply include an endpoint with the fixed-header input _after_ the one with the fixed-header requirement.

One nice feature of tapir is that it’s quite easy to create endpoints which have the same definition across versions, without duplicating code, e.g.:

```auto
def myEndpoint(version: String) = 
  endpoint.in("api" / version / "user").in(...)

```

What I would not recommend is doing versioning based on a field in the body. In general, it’s better to perform routing using information which is available upfront for a request (method, path, query parameters, headers), so that any middleware components can perform their task without a need to read & parse the body. This is especially important if the body can be large and should be streamed (instead of reading it into memory entirely), but also complicates the implementation when it comes to avoiding having to parse the body twice (once for routing, second time for the server logic).

However, if the endpoints are otherwise identical, and differ only in the body, I think a `oneOf` input with variants corresponding to the body versions should work as well. When decoding a request, the variant which decodes successfully is chosen - so the task of checking if the version matches would be delegated to the JSON decoder. You could start with a `oneOf[Either[OldBody, NewBody]]` and provide variants for `Left`/`Right` cases. In this approach, the OpenAPI docs would include a single endpoint, with a `oneOf` body specification.

I don’t think there’s a “recommended” way of doing API versioning, just different tradeoffs when it comes to structuring the application’s code and presenting the documentation. Hope this helps 🙂
