In this section, we are going to learn how to use Swagger to document APIs. Swagger is an API manager that follows the Open API standard, so that it is a common language for all the API creators. We will discuss how to write definitions and why it is so important to agree on how to describe resources.
Documentation is always a problem. No matter how hard you try, it will always eventually go out of date. Luckily, in the past few years, there has been a push into producing a high quality documentation for REST APIs.
API managers have played a key role in it, and Swagger is particularly an interesting platform to look at. More than a module for documentation, Swagger manages your API in a such way that gives you a holistic view of your work.
Let's start installing it:
npm install -g swagger
This will install Swagger system-wide, so it will be another command in our system. Now, we need to create a project using it:
swagger project create my-project
This command will allow you to choose different web frameworks. We are going to choose Express, as it is the one that we have already been using. The output of the preceding command is shown in the following screenshot:
Now we can find a new folder, called my-project
, that looks like the following image:
The structure is self-explanatory and it is the common layout of a Node.js application:
Swagger comes with an impressive feature: an embedded editor that allows you to model the endpoints of your API. In order to run it, from within the generated folder, execute the following command:
Swagger project edit
It will open Swagger Editor in the default browser, with a window similar to the following image:
Swagger makes use of Yet Another Markup Language (YAML). It is a language that is very similar to JSON, but with a different syntax.
In this document, we can customize a number of things, such as paths (routes in our application). Let's take a look at the path generated by Swagger:
/hello: # binds a127 app logic to a route x-swagger-router-controller: hello_world get: description: Returns 'Hello' to the caller # used as the method name of the controller operationId: hello parameters: - name: name in: query description: The name of the person to whom to say hello required: false type: string responses: "200": description: Success schema: # a pointer to a definition $ref: "#/definitions/HelloWorldResponse" # responses may fall through to errors default: description: Error schema: $ref: "#/definitions/ErrorResponse"
The definition is self-documented. Basically, we will configure the parameters used by our endpoint, but in a declarative way. This endpoint is mapping the incoming actions into the hello_world
controller, and specifically into the hello
method, which is defined by the
id
operation. Let's see what Swagger has generated for us in this controller:
'use strict'; var util = require('util'); module.exports = { hello: hello }; function hello(req, res) { var name = req.swagger.params.name.value || 'stranger'; var hello = util.format('Hello, %s!', name); res.json(hello); }
This code can be found in the api/controllers
folder of the project. As you can see, it is a pretty standard Express controller packed as a module (well-cohesioned). The only strange line is the first one in the hello
function, where we pick up the parameters from Swagger. We will come back to this later, once we run the project.
The second part of the endpoint is the responses. As we can see, we are referencing two definitions: HelloWorldResponse
for http code 200
and ErrorResponse
for the rest of the codes. These objects are defined in the following code:
definitions: HelloWorldResponse: required: - message properties: message: type: string ErrorResponse: required: - message properties: message: type: string
This is something really interesting, although we are using a dynamic language, the contract is being defined by Swagger so that we have a language-agnostic definition that can be consumed by a number of different technologies, respecting the principle of technology heterogeneity that we were talking about earlier in Chapter 1, Microservices Architecture, and Chapter 2, Microservices in Node.js – Seneca and PM2 Alternatives.
After explaining how the definition works, it is time to start the server:
swagger project start
This should produce an output that is very similar to the following code:
Starting: C:my-projectapp.js... project started here: http://localhost:10010/ project will restart on changes. to restart at any time, enter `rs` try this: curl http://127.0.0.1:10010/hello?name=Scott
Now, if we follow the instructions of the output and execute the curl command, we get the following output:
curl http://127.0.0.1:10010/hello?name=David "Hello David!"
Swagger is binding the name
query parameter to the Swagger parameter specified in the YAML definition. This may sound bad, as we are coupling our software to Swagger, but it gives you an immense benefit: Swagger allows you to test the endpoint through the editor. Let's see how it works.
On the right-hand side of the editor, you can see a button with the Try this operation label, as shown in the following screenshot:
Once you click it, it will present you a form that allows you to test the endpoint, as shown in the following screenshot:
There is a warning message on this form about cross-origin requests. We don't need to worry about it when developing in our local machine; however, we could have problems when testing other hosts using the Swagger Editor.
Enter a value for the name parameter, and after that, click on Send Request, as shown in the following image:
Be aware that, for this test to work, our app server has to be up and running.
Until now, we have been playing with Swagger and the generated project, but we are now going to generate the project from the swagger.yaml
file. We will use the already generated project as a starting point, but we will add a new endpoint:
swagger: "2.0" info: version: "0.0.1" title: Stop Words Filtering App host: localhost:8000 basePath: / schemes: - http - https consumes: - application/json produces: - application/json paths: /stop-words: x-swagger-router-controller: stop_words get: description: Removes the stop words from an arbitrary input text. operationId: stopwords parameters: - name: text in: query description: The text to be sanitized required: false type: string responses: "200": description: Success schema: $ref: "#/definitions/StopWordsResponse" /swagger: x-swagger-pipe: swagger_raw definitions: StopWordsResponse: required: - message properties: message: type: string
This endpoint might sound very familiar to you, as we unit tested it earlier in this chapter. As you probably know by now, the Swagger Editor is quite cool: it provides feedback as you type on, about what is going on in the YAML file, as well as saves the changes.
The next step is to download the Swagger code generator from https://github.com/swagger-api/swagger-codegen. It is a Java project, so we are going to need the Java SDK and Maven to build it, as follows:
mvn package
Codegen is a tool that allow us to read the API definition from the Swagger YAML and build the basic structure for a project in a language of our choice, in this case, Node.js.
The preceding command in the root of the project should build all the submodules. Now, it is as easy as executing the following command in the root of the swagger-codegen
folder:
java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate -i my-project.yaml -l nodejs -o my-project
The Swagger code generator supports a number of languages. Here, the trick is that when using it for microservices, we can define the interface and then use the most appropriate technology to build our service.
If you go to the my-project
folder, you should find the full structure of the project in there, ready to start coding.