0.0.0-experimental-c468054 • Published 2 years ago

api-versioning v0.0.0-experimental-c468054

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

api-versioning

npm

It is an express based api versioning tool with inheritance and override system.

Why?

It was created to facilitate versioning of routes based on inheritance from previous versions. Example:

When we want to access a feature of a certain version, we just make a request for that feature curl <host>:<port>/api/resources.

But when our api is open we need to start versioning it so as not to break any consumer that uses the version of that old resources curl <host>:<port>/api/v1.0/resources.

As we grow our api starts to have a group of bigger and bigger versions.

curl <host>:<port>/api/v1.0/resources

curl <host>:<port>/api/v1.1/resources

curl <host>:<port>/api/v1.2/resources

...

curl <host>:<port>/api/v2.0/resources

If we are going to analyze the code of this we will have something like this:

import Express from 'express'

const app = Express()

const v1_0 = Express.Router()
const v1_1 = Express.Router()
const v1_2 = Express.Router()
// ...
const v2_0 = Express.Router()

const api = Express.Router()

api.use('/v1.0', v1_0)
api.use('/v1.1', v1_1)
api.use('/v1.2', v1_2)
// ...
api.use('/v2.0', v2_0)

app.use('/api', api)

If we don't do any manual implementation guiding the current version to have the previous versions in its route basically when we are going to try to access an old resource that has not been overwritten by the current version it will return a 404 because the resource does not exist in the requested version.

v1_0.get('/users', /** <handler logic> */)
v1_0.get('/clients', /** <handler logic> */)

v1_1.get('/users', /** <handler logic> */)
v1_1.get('/clients', /** <handler logic> */)

v1_2.get('/clients', /** <handler logic> */)

v2_0.get('/users', /** <handler logic> */)

In the above case if you try to access:

curl <host>:<port>/api/v1.2/clients

curl <host>:<port>/api/v2.0/users

They will return a 404, because their implementation does not exist in the requested version.

To avoid this, we can tell the handler that the version in question uses its previous versions.

In order not to have to think about this behavioral logic, the library was created.

Usage

really trivial, put your routes in their respective versions.

import Express from 'express'
import versioning from 'api-versioning'

const app = Express()

const v1_0 = Express.Router()
const v1_1 = Express.Router()
const v1_2 = Express.Router()
// ...
const v2_0 = Express.Router()

const api = Express.Router()

// ...
api.use('/api', versioning({
  'v1.0': v1_0,
  'v1.1': v1_1,
  'v1.2': v1_2,
  // ...
  'v2.0': v2_0,
}))

Troubleshooting versions

When you use the library resources, you automatically get the default api fallback which is the latest version of your api.

If you have versions v1.0, v1.1, v1.2 and v2.0. The request will drop to v2.0 by default if the version has not been requested or configured.

If the request has requested a version that doesn't exist, it will get the one closest to its bottom.

If you have versions v1.0, v1.1, v1.2 and v2.0. The request requests version v1.3 it will drop down to version v1.2 which is closest down to it. You can configure this behavior with the strategy option.

enum Strategy {
  Ceil,
  Floor,
  StrictCeil,
  StrictFloor,
  None,
}

If you have versions v1.0, v1.1, v1.2 and v2.0

StrategyRequestedBehavior
Ceilv0.9Will fall on v1.0
Ceilv2.1Will fall on v2.0
Floorv1.3Will fall on v1.2
Floorv0.9Will fall on v1.0
StrictCeilv0.9Will fall on v1.0
StrictCeilv2.1Will call the next handlers
StrictFloorv0.9Will call the next handlers
StrictFloorv2.1Will fall on v2.0
Nonev0.9Will call the next handlers
Nonev2.1Will call the next handlers

Inheritances

If the feature is not found in the requested version it will see the version downgrade until it finds the feature otherwise it will call the next handler.

v1_0.get('/users', /** <handler logic> */)
v1_0.get('/clients', /** <handler logic> */)

v1_1.get('/clients', /** <handler logic> */)

If you call the resource (curl <host>:<port>/api/v1.1/users), it will fall into the version 1.0 handler as the resource doesn't exist in version 1.1.

But that's not all, you can even handle the request that will fall on version 1.0.

v1_1.get('/users', (req, res, next ) => {
  // <handler logic>
  next('version')
})

That way you are calling the previous version so that it uses the request you just handled.

There are some settings that can change this behavior.

enum Heritage {
  All,
  Degree,
  None,
}

The All is exactly the behavior I've just described, with it we're going to look for ALL of its versions until this feature is finally found.

Degree is a little more regulated and with it you will only be able to get resources that share the same "major" in the version. Example:

Version 1.2 is able to extend features from version 1.1 and 1.0. But when the version has a major major it won't look in previous versions. Version 2.0 will not look for 1.2 or lower versions.

With the None no one will simply inherit.

License

api-versioning is MIT licensed.