sails-hook-api-version-accept
v0.1.0
Published
Sails.js hook that enabled API versioning through the Accept header
Downloads
62
Maintainers
Readme
A Sails.js hook that enables API versioning via the HTTP Accept header. It forces clients to specify the version and routes requests for older versions to Sails.js helpers.
Still alpha. Not ready for use yet.
How to use this hook
- install the hook into your project
yarn add sails-hook-api-version-accept
- enable the hook in your
.sailsrc
by listing it as a hook:/* .sailsrc */ { "generators": {... "_generatedWith": {... "hooks": { "api-version-accept": true /* <- add this line */ } }
- on each of your models that you want versioned, you need to add the
versionConfig
fieldmodule.exports = { attributes: {... versionConfig: { /* <- add this config, see next section for help */ versions: ['v1', 'v2'], vendorPrefix: 'vnd.techotom', } }
- for versions that aren't the latest, you need to define some helpers to service those requests. Each helper will match the pattern:
api/helpers/<version>-<model>-<action>.js
. For example, forversion=v1
,model=Foo
andaction=Find
we would create a helper with the file name:api/helpers/v1-Foo-Find.js
. You can create the file by hand or use the sails generator:sails generate helper v1FooFind
- implement the helper(s) you've just created. You can do this however you see fit. As an example, to implement a
find
action for an older version, it makes sense to call the.find()
action (effectively getting the results for the latest version), then transform the result before sending the response. Seedemo/api/helpers/v1FooFind.js
for an example. - update any clients for this API so they send the correct
Accept
header.
Reference for versionConfig
object
| field name | type | example | description
|---|---|---|---|
| versionConfig
| {}
| {versions: [], vendorPrefix: 'string'}
| The container object for the config
| versionConfig.versions
| string[]
| ['v1', 'v2']
| Each element represents a version. Make sure your names have legal syntax for Content-types. The order of element is important; the last element is implicitly the latest version. The recommendation is to keep it simple: ['v1', 'v2', 'v3', ...]
.
| versionConfig.vendorPrefix
| string
| vnd.example
| It is suggested that you use the vendor tree prefix of vnd.
and then some subtree of that for your project. You're meant to register them with IANA but you can probably write your API first and worry about that later when it starts to take off. This value will be inserted into the MIME like application/<vendorPrefix>.v1+json
.
Why create this hook?
RESTful API Versioning has a number of schools of thought, you can read some background at the following links:
- https://www.troyhunt.com/your-api-versioning-is-wrong-which-is/
- https://www.narwhl.com/content-negotiation/
Troy captures the different schools of thought concisely:
...let me outline the three common schools of thought in terms of how they’re practically implemented:
- URL: You simply whack the API version into the URL, for example: https://haveibeenpwned.com/api/v2/breachedaccount/foo
- Custom request header: You use the same URL as before but add a header such as “api-version: 2”
- Accept header: You modify the accept header to specify the version, for example “Accept: application/vnd.haveibeenpwned.v2+json”
I like option 3. I also like Sails.js. This repo is my attempt to add support for API versioning whilst also keeping all the things that make Sails awesome.
What this hook does
This hook tries to leave as much as possible to pure Sails and only step in when required. This means:
- you define version config on the models you want versioned
- requests for the latest version, or
*/*
, just use the Sails Blueprint or whatever default handler you have. The only difference is this hook will force theContent-type
response header to be the correct, versioned MIME. - requests for earlier versions rely on Sails helpers that you must write
Assumptions
- you're using the hook from the start for a new project. You can use it for existing projects, it might require more work to add though.
- you have Blueprint shadow routes enabled
- your Sails.js project is basically a HTTP API, i.e. you selected
Empty
when you generated a new Sails project. No testing against a "full" Sails web app (with MVC) has been done yet.
TODO
- work out how to handle deleted fields in a given Model. This requires that the model can still store the old field but requests for newer versions don't include it. Perhaps we can add an optional config param that defines which version is the
default
, so you can pin that to a version and write helpers for newer versions. - handle the blueprint association routes
- work out how to handle version POST (Create) requests. Older clients will want to send what they've always sent but that might not be valid for the newer schema. Perhaps we can ask the API developer to write a helper and dynamically add it as a beforeCreate hook for the model. The fallback and be to reject the request.