-
-
Notifications
You must be signed in to change notification settings - Fork 494
Description
Motivation
When building APIs for applications (especially SaaS), changes happen, new features are added, bugs are fixed, and at some point, breaking changes inevitably show up.
Some of these modifications over the course of development can break existing integrations or impact clients that depend on the current API contract. That's why API versioning is important: it allows older versions to keep working while newer versions continue to evolve.
Currently, in Elysia, this can be done manually using prefixes or route groups:
app.group("/v1", (app) => {
app.use(userRoutes)
})
app.group("/v2", (app) => {
app.use(userRoutesV2)
})This approach works well for simple cases, but doesn't cover some common patterns used in production APIs, such as:
- Header-based versioning > keeping URLs clean and defining the version via an HTTP header
- Clear errors for unsupported versions > when a client requests a version that doesn't exist
- Deprecation of older versions > using HTTP headers like
DeprecationandSunset - Separate documentation per version > making the API easier to use when multiple versions exist
Adding optional versioning support could greatly improve the DX for anyone who needs to evolve APIs over time without breaking existing applications.
Proposal
The idea is to add optional API versioning support to Elysia.
The goal is not to replace route prefix usage, but to offer a more structured way to handle versioning.
Implementation as a plugin
An interesting approach would be to implement this as a plugin, for example @elysiajs/versioning.
This would have a few advantages:
- keeps the framework core simple
- follows Elysia's modular philosophy
- lets the community test and evolve the idea
- completely optional adoption
If the solution works well, it could eventually be integrated into the core.
Large APIs, like Stripe's, use structured versioning strategies precisely to be able to evolve without breaking clients.
Proposed features
Versioning strategies
The plugin could support different ways of defining the API version.
Header-based versioning
The API version is defined in a request header:
app.use(versioning({
strategy: "header",
header: "Accept-Version",
defaultVersion: "1"
}))Example request:
GET /users
Accept-Version: 2
Path-based versioning
The version appears directly in the URL:
app.use(versioning({
strategy: "path",
defaultVersion: "1"
}))Example:
/v1/users
/v2/users
Query-based versioning
Useful for testing or internal APIs:
app.use(versioning({
strategy: "query",
parameter: "version",
defaultVersion: "1"
}))Example:
GET /users?version=2
Version on routes
Routes could define which version they belong to:
app.get("/users", handlerV1, {
version: "1"
})
app.get("/users", handlerV2, {
version: "2"
})With this:
- multiple versions can exist for the same route
- the framework automatically picks the correct handler
- routes without a version continue to work normally
Version deprecation
It would also be possible to mark routes or versions as deprecated:
app.get("/users", handler, {
version: "1",
deprecated: {
sunset: "2027-06-01"
}
})Responses could include headers such as:
Deprecation: true
Sunset: Mon, 01 Jun 2027 00:00:00 GMT
This helps clients understand when they need to migrate to newer versions.
Error for unsupported versions
If a client requests a version that doesn't exist, the framework could return a clear error:
{
"error": "Unsupported API version",
"requestedVersion": "5",
"supportedVersions": ["1", "2"],
"latestVersion": "2"
}This avoids unexpected behavior and makes debugging easier.
Per-version documentation
Integration with plugins like @elysiajs/swagger could generate separate documentation per version:
/docs/v1
/docs/v2
Or even allow choosing the version within the documentation interface itself.
Backward compatibility
This proposal is fully compatible with existing applications:
- defining a version on routes is optional
- existing routes continue to work normally
- versioning is only applied when the plugin is used
Benefits
Some benefits of this approach:
- better DX for APIs that need to evolve
- less boilerplate for handling versioning manually
- patterns used in real-world APIs
- integration with the Elysia ecosystem
- ability to evolve APIs without breaking clients
Open for discussion
This is an initial idea to discuss how structured versioning could work in Elysia.
A few questions:
- would this make more sense as a plugin or as a core feature?
- are there other versioning strategies worth considering?
- how can this be better integrated with documentation plugins?
If the maintainers find the idea interesting, I'd be happy to help with the design or implementation.