On one of my recent projects I’ve been asked to describe how our Restful API can be consumed by a third party service. In a SOAP world this task usually boils down to providing a WSDL, which can simplify understanding of exposed API, and can also be used for generating API clients in a most standardized manner.
In REST world there is still chaos in specification tools, standards and best-practices. One feeling totally overwhelmed by the number of choices might stick to the classic – manually written API specification document and a couple of request/response examples. This approach, however, along with a lot of manual effort has another major downside – specification document is static and needs to be updated every time your API changes.
If your API is still on the way to maturity Level 3 according to Richardson Maturity Model (not driven by hypermedia, not self-descriptive) and you are looking for a tool that allows generating interactive, good looking documentation, which is up-to-date with your API, than a tool like Swagger is what you may want to consider.
What is Swagger
Swagger is not only a specification for describing REST APIs, but also an ecosystem of tools for visualizing, producing and consuming RESTful web services. It is meant to enable your APIs to always stay in sync with the documentation; this is achieved by tightly integrating documentation metadata into the code.
Show me the code
I'll omit the configuration part, which is different for Spring MVC and JAX-RS, and will show the basic idea on how swagger metadata is integrated into the server code.
In order to make your Resource/Controller discoverable by Swagger you need to add Swagger annotation @API to your Controller class:
@RestController
@RequestMapping("/merchants")
@Api(basePath = "/merchants", value = "Merchants", description = "Operations with Merchants")
public class MerchantController {
...
}
In order to provide meaningful description on what each of the API operations does, we need to annotate class methods with @ApiOperation annotation:
@ApiOperation(value = "Retrieve Merchant", notes = "Returns Merchant details by identifier", response = Merchant.class)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Merchant successfully retrieved", response = Merchant.class),
@ApiResponse(code = 404, message = "Merchant not found")
})
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Merchant getMerchant(@ApiParam(value = "merchant id", required = true) @PathVariable("id") Long id) {
...
}
As you can see providing additional metadata for operation parameters, response types and response codes is as simple as adding annotations.
This method returns Merchant class, which can also be documented:
@ApiModel
public class Merchant {
@ApiModelProperty(required = true, value = "Name of the Merchant company")
private String companyName;
...
}
As a result, generated API specification should be exposed as http://localhost:8080/v2/api-docs/, and it should look like this:
{
"swagger": "2.0",
"info": {},
"host": "localhost:8080",
"basePath": "/",
"tags": [{"name": "merchants", "description": "Operations with Merchants"}],
"paths": {
"/merchants/": {
"put": {
"tags": ["merchants"],
"summary": "Updates merchant details",
"operationId": "updateUsingPUT",
"consumes": ["application/json"],
"produces": ["*/*"],
"parameters": [{
"in": "body",
"name": "settings",
"description": "Merchant details",
"required": true,
"schema": {"$ref": "#/definitions/Merchant"}
}],
"responses": {
"200": {"description": "Merchant updated", "schema": {"$ref": "#/definitions/Merchant"}}
}
}
},
"/merchants/{id}": {
"get": {
"tags": ["merchants"],
"summary": "Retrieves Merchant",
"description": "Returns Merchant details by identifier",
"operationId": "getMerchantUsingGET",
"consumes": ["application/json"],
"produces": ["*/*"],
"parameters": [{
"name": "id",
"in": "path",
"description": "merchant id",
"required": true,
"type": "integer",
"format": "int64"
}],
"responses": {
"404": {"description": "Merchant not found"},
"200": {
"description": "Merchant successfully retrieved",
"schema": {"$ref": "#/definitions/Merchant"}
}
}
}
}
},
"definitions": {
"Merchant": {
"required": ["companyName", "companyVatNumber"],
"properties": {
"companyName": {"type": "string", "description": "Name of the Merchant company"},
"companyVatNumber": {"type": "string", "description": "Company VAT number in format LV2346234"}
}
}
}
}
Swagger is able to pull out all the metadata (data types, formats) not only from annotations, but also implicitly from the context.
So what's next?
Once your API specification is generated, it can be used to:
- generate interactive documentation using swagger UI – pet store example,
- generate static documentation (PDF, Markdown),
- generate client libraries.
Need any assistance with your RESTful APIs? Give us a hint, we can help!
About the author: Aleksandrs Kuripka has been with Idea Port Riga since March 2011, working with Java technologies and custom development in general for almost 10 years since the beginning of his career. Being a keen supporter of lean, agile and clean code principles, he tends to keep the code base habitable and focus on things that bring real value to the customer. |