This article was originally posted on my Medium blog.
I always found the hardest thing about API work was the documentation.
Sure, there are loads of nice tools out there to help you define it, provide nice front-ends and the like, but maintaining that isn’t anywhere nearly as fun as getting the actual work done. So soon enough, you’ve got stale documentation with little errors, and validation rules that don’t quite match up.
A recent NodeJS API project came my way which had out-of-date OpenAPI 3 documentation for the few endpoints it already had, but the understanding that we where going to start using it a lot more, so it needed to get up to scratch.
OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs
I figured that if we where going to maintain this OpenAPI spec, which contained all the validation rules for the endpoints, then there must be a way we could use that to save us some time.
What if we could use that spec to enforce the validation? What if we could use it as the basis of the endpoint testing?
If we could get these two things, we have the wonderful combo of the OpenAPI spec needing to be write for the validation to work, and the validation being unable to deviate from the spec — so no more dodgy documentation where that param is documented as an int but it’s actually a float..
.. and if we can build tests based from the documentation then all our outputs have to be as defined, so the consumers of the API don’t get urked if we send an object and they’re expecting an array.
Using the OpenAPI spec to enforce the validation and be the crux of the tests enforces good definition of the API and removes all the nasty little ‘Ohh yea, that only returns X if Y’ that plagues API development IMHO.
So let’s stop waffling here and create something simple to prove how this works.
First we’re going to spec our endpoint. To save some time, I’ve used one of the sample specifications as a base. There’s a very nice editor / visualization tool at https://editor.swagger.io/ to work with your spec files.
Here’s the sub-set of the specification we’re going to look at:
An endpoint that expects two variables in the path, {dataset} and {version} that are both strings. There are three possible variables in the post body also, one of which is required. It has two responses, a 200 that returns an array of records, and a 404. The response has a bunch of criteria also.
Let’s store this thing as /spec/api.spec.yaml
Now, a quick build of an Express app to handle responses to the path as documented:
This is as simple as it gets. So lets run it and check out if it works in Postman.
This all so-far-so-normal. Lets add the good stuff. Looking at the spec, we should now start adding validation into the endpoint we’ve just created — ensuring all those numbers are numbers, that the criteria is there etc.. But we don’t want to do that as we’ve already spent the time writing that all into the spec.
We’re going to install a node module called express-openapi-validate ( along with js-yaml) to handle this for us. Now we’ve got that installed, let’s change up the code a little:
Lost more going on here!
We’ve loaded the app.spec.yaml file and we’re creating an OpenApiValidator object with it, along with some interesting looking options. The options are all inherited from the parsing app ajv . Our two are :
allErrors: true, // makes it return all errors, not just the 1st
removeAdditional: "all", // Removes any undocumented params
We’re validating the spec against the request as middleware, where we’re telling it what method we’re looking for and the path, and we’ve added some error handling to give us something to display if it doesn’t all go according to plan.
Let’s mess up our request, and try again.
Okay! We’ve just added validation against the OpenAPI spec! It’s capturing the two things I broke: The removal of the required field criteria , and the incorrect type of .body.rows . It’s not a very graceful error message, but it’s telling the consumer what’s gone wrong, and you’ve not had to write any of it. It’s also returning the right status code of 400 for us. Sweet!
Let’s fix the request and try once again.
All looks as before.. but it’s stripped out the foo: "bar" from the body, because it wasn’t documented. The validator stripped it out because it was undocumented. No more sneaking in properties in post bodies and not telling anyone.
This means now, if you format your OpenAPI spec correctly, the data arrives to your code validated and correct. Now, I’m not saying it’s perfect — there is a known problem with trying to parse numbers in the path, and Express handles everything as a string, but it’s much faster than having to maintain the OpenAPI spec document -and- the validation on the endpoint.
I’m hoping that gives you enough grounding in how to approach this so you can start using your OpenAPI spec documents as the amazing resource that it is. Treat the spec well and it’ll not only provide you with documentation for the consumers of the API, but will also do a lot of the work for you.
The next thing to look at, which I’ll link to once I write it, is the other side of this, which is writing tests that ensure that the output of your API conforms to the OpenAPI spec — thus forcing you to write API responses that your consumers expect!
I would love to hear how you use this in your projects! Get me on https://twitter.com/Scampiuk
Top comments (1)
Good one, thnaks!