http-rs / http-rs/tide

endpoint versioning

Open
#524 1 comment 7 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
5.1k
Forks
329
PR merge metrics
No merged PRs in 30d

Description

Fastify has a fun way of [versioning endpoint](https://www.fastify.io/docs/latest/Routes/#version) using `Accept-Version` headers. The way it's declared looks like this:

```js
fastify.route({
method: 'GET',
url: '/',
version: '1.2.0',
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
```

I was thinking this could be an interesting mechanism for Tide as well. What if we could switch logic based on API version headers like this:
```rust
let mut app = tide::new();

app.at("/user")
.get(|_| async move { Ok("user v2.3.0") })
.version("1.2.0").get(|_| async move { Ok("user v1.2.0") })
.version("2.3.0").get(|_| async move { Ok("user v2.3.0") });
```

The `version` param would switch based on the `Accept-Version` header and the `Accept: version="..."` header. We'd need to figure out the specifics of how to integrate, but I think this could provide an exciting mechanism for *versioned* endpoints. Which are something that are incredibly important for applications.

## Alternatives

The [APIs as infrastructure](https://stripe.com/blog/api-versioning) post by Stripe describes an interesting practice they use to provide upgrades to their APIs. This seems like a lot of work to maintain, but it's interesting to think how we could support it. One way we could perhaps do this is if we continued to lean into [`str::find`-like](https://doc.rust-lang.org/std/primitive.str.html#method.find) APIs: APIs that can take different types of arguments, including closures. Defining the API part of Stripe's upgrade logic could then in Tide be defined as:

```rust
user tide::version::Date;

let mut app = tide::new();

let latest = |_| async move { Ok("user v2.3.0") };
let change_body = |res| async move { res.set_body("user v1.2.0"); res };

app.at("/user")
.get(latest)
.version(Date::new("2020-01-30").get(|_| async move {
let res = latest().await?;
Ok(change_body(res).await?)
})
.version(Date::new("2020-03-30").get(latest);
```

This is a super rough sketch of what it could look like, but the overall point I'm trying to convey is that there's a way the design above could be made forward compatible with some really elaborate versioning schemes. Thanks!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.