Whether it’s a stand-alone service, a microservice or a back end for a web application, a consistent and robust API is no longer something to be handcrafted. Using an API framework likeSwagger promises to give you a leg up and help you build a robust API for less cost.
In our current work, we want to bring up an API as a back end for a web application. The API isn’t the be-all and end-all of the application we are building, but it needs to be robust, secure and flexible enough to accommodate changes as we go.
When setting out to build an API from scratch, there are decisions to make and additional implementation work to consider. For example, on the server side we’re looking at patterns for server architecture, authentication, authorization and request validation. On the client side we need to think about writing a service layer that encapsulates HTTP requests and related logic. Finally, we need to define the details of how to transmit data in requests and responses, as that’ll be more expensive to change as the API grows.
When considering all these issues, we immediately get the feeling that we are about to spend time and effort writing and testing code that doesn’t really add value to our application. Surely we can short-circuit some of that work.
Swagger will give you a leg up with validation, security and a consistent but not necessarily opinionated back-end structure for your server. If that were not enough, dynamic client-side and server-side code generation is available, which can save additional development effort.
There is an initial learning curve but beyond that more of your effort is directed into tightly defining the API via the Swagger specification and implementing and testing the route handler logic. With the Swagger toolset doing the rest, security and validation features are likely more consistent and robust than handcrafted code would be and you’ll spend next to no time implementing boilerplate code.
The biggest wins we’ve found with Swagger so far are:
- There is less implementation work and less code to maintain—particularly through the generated client library and request/response validation, both of which essentially come out of the box, driven by the Swagger specification we create.
- We are unconcerned with the details of how our API works over HTTP; we can set out adding data in headers, query and message body, but easily restructure that at any point without having to make code changes. The client-side library hides that detail, and all request parameters are normalized into a single location on the server, no matter how they were transmitted.
- The “specification-focused” work of defining the API constantly helps us with consistency. For example, just maintaining a DRY Swagger document as it grows inevitably means using response definitions, which in turn helps ensure that standard API responses are consistent across all calls. We get specification reuse to match the code reuse we strive for in our handlers.
Swagger is here to stay as part of my API toolset, and I’ll share some of the nuts and bolts of bringing up a Swagger API Node/Express 4 server consumed by a JS client in the browser.
Swagger Is Here to Stay
Swagger is an open-source framework (Apache 2.0) in its second major release. Since January 1, 2016, Swagger has been under the care of the Open API Initiative, a Linux Foundation Project backed by a significant industry consortium. It’s well supported and here to stay.
Swagger implements the Open API specification, which is the language used for defining your APIs in the tool. Given a Swagger definition in YAML or JSON, it provides a toolset and various libraries and middleware to bring up your environment quickly.
As with any rapidly evolving open-source project, within the Swagger ecosystem there are some stale-looking projects as well as some gaps and confusion in the evolving documentation. Despite that, there is a solid core toolset maintained by the Swagger API team, and I’d recommend starting there.
The core of Swagger is built on Java, but we are of course using the Node toolchain.
Swagger node.js CLI
With Node and npm installed, the first option to get an API up quickly is to use the Swagger-NodeCLI tool. After `npm i swagger -g –save-dev`, use the `swagger` command to generate an opinionated project layout for an Express API server.
The server layout is quite minimalistic, so it may be a good jumping-off point if you are starting from scratch. However, if you’re like us and you’re building on top of a more feature-rich starter kit, then the CLI output is more instructional.
The second option to get moving fast is to use the online Swagger Editor, put together by the core Swagger team. If you are fine with using opinionated server code and especially if you can define all or a good part of your API up front, then you can bring an API and server up very quickly.
Step 1: Define API Spec
Head over to the editor and define your API spec in there. Beyond syntax highlighting, there is a very useful built-in validation feature that interactively checks your Swagger specification and flags errors in the right-hand pane.
It’s invaluable. So even if you are editing elsewhere, circle back here to check your specifications and troubleshoot.
Step 2: Generate Server Code
The editor is the easiest place to generate server code at this point. We’ve found the documentation around generating server code locally a bit confused. Via the editor, it works well and 18 language/framework pairs are available.
Step 3: Generate Client Code
Pick one of the 30+ client language/framework options to get a ready-to-go compact client library for your API. How much coding have we avoided so far?
Step 4: Start Your Server
Step 5: Query the API
Sit back and enjoy querying your API. At this point all handers in the server-generated code are even returning mock data based on your specification.
This shows the huge advantage of Swagger to get you a working skeleton implementation from the specification alone.
Integrating Swagger with Express
Now, let’s say we don’t want to start from scratch with Swagger-generated server-side code, but instead want to use Swagger within an Express server architecture that we already have primed. Integration is straightforward and the generated server-side output from the Swagger Editor is still informative.
There are 4 things to do:
- Create and populate a default swagger.yaml
- Generate any client libraries you need
- Plug in the middleware
- Configure your client
1. Default swagger.yaml
We’ll talk about the YAML spec more below, but getting oriented and working from a starting template can help. Best examples are all available for loading in the Swagger Editor, including a couple that define real APIs such as Twitter and Instagram.
2. Generating Client Libraries with Swagger Codegen
There are a few different middleware options including some contributions that look like they’ve gone cold maintenance-wise.
We’ve settled on using the `swagger-tools` middleware set that comes in as a single npm module and provides middleware functions for metadata, security, validation and routing, and bundlesSwagger UI into Express to boot.
Swagger extends the Express request object, so that each route handler has access to incoming parameters that have been parsed based on the spec, as well as additional Swagger-generated information from the client.
Any incoming parameters for the API call will be available in `req.swagger` regardless of whether they were transmitted using query, body, header, etc. (See the middleware docs for more details.)
Validation middleware will only route requests that match paths in Swagger specification exactly in terms of endpoint path, request mime type, required and optional parameters, and their declared types.
Response Validation can also be enabled, which helps validate the behavior of route handlers and logic on the server. This means you must complete the response sections of the paths correctly and honor the mime type of your responses.
With request/response validation added, Swagger is doing the bulk of heavy lifting that’ll keep your API robust and consistent.
The Swagger Router connects the Express route handlers found in the controller files on the path specified, with the paths defined in the Swagger specification. The routing looks up the correct controller file and exported function based on parameters you add to the Swagger spec for each path.
Here is an example for a hypothetical to-do app:
The fields `x-swagger-router-controller` will point the middleware to a `todos.js` file in the controller’s directory, while the `operationId` names the handler function to be invoked.
The final piece of middleware enables serving of the swagger-ui interface direct from the Express server. It also serves the raw Swagger schema (.json) that clients can consume. Paths for both are configurable.
The Swagger security middleware is available here too, and we use this in conjunction with Auth0 in our projects. We’ll cover that in a later post.
4. Configure Your Client
swagger-js is the recommended starting point if you are developing in the browser. An important point to bear in mind is that the client library is built dynamically based on the swagger document (swagger.json) exposed by the server.
If for some reason that’s not attractive, then the node.js client library is available; if you’re using Browserify or webpack, you’ll be able to utilize that.
Configuring the client library and making a call is simple:
The client provides a promise-based interface and saves you from writing any http server layer code at all. A bigger advantage is that it actually hides this concern from your client application altogether.
Should you decide to change the mechanics of how the underlying http requests are made—for example using query strings for parameters over the body, or moving certain data into custom informational headers—these changes can be made to the spec without the client application code having to change or care.
At this point we’ve taken you through the basics of getting an API up and running using the core Swagger toolset. We focused on building a back end for a web application, but practically everything we have covered also applies to bringing up stand-alone API.
There is still a lot to cover with respect to using Swagger in practice, so stay tuned for more posts on authentication with Auth0, a deeper dive on the Swagger specification and implementing server-side logic.