Are OpenAPI and Swagger trying to turn REST into SOAP?
14 August 2017
The objective of Swagger is to create a “RESTful contract for your API, detailing all of its resources and operations in a human and machine-readable format.” In this sense it is a functional equivalent of WSDL documents for SOAP, providing automatically generated descriptions that make it easier to discover and integrate with REST APIs.
The problem is that this kind of automatically generated contract isn’t necessarily a great fit for RESTful APIs. In practice it can undermine some of the main features of REST, reducing APIs to a form of RPC contract that feels like a less reliable version of SOAP and WSDL.
Swagger is not RESTful
A problem with Swagger is that it doesn’t describe REST adequately. More specifically, it does not directly support hypermedia – a key aspect of REST that allows servers to control their own namespace. Servers should not be bound by fixed URLs but be allowed to define them in resource responses. A client should only need to know the root URL of an API and be able to navigate the remainder on responses alone.
Roy Fielding has been particularly clear on this subject:
...if the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period.Swagger focuses very much on functions that are available via specific URI patterns. In this respect it reduces REST to RPC over HTTP by defining a remote data model that is manipulated through function calls. It does make a nod to hypermedia in version 3.0 of the specification by supporting static link definitions, but it makes it clear that hypermedia-driven APIs are not a goal.
This criticism of Swagger does risk descending into an unhelpful debate over what is and is not “RESTful”. One of the drawbacks of implementing REST APIs is that clients do not always leverage hypermedia, preferring to format URIs directly according to the patterns they expect. Swagger encourages this behaviour by focusing on the available URI patterns. In this sense it undermines REST by exasperating an on-going problem with the way REST APIs are adopted.
The perils of generated clients
There is a hangover from the SOAP era where client applications could be automatically generated from a WSDL contract that defined all the service functions and data. The resulting client applications could be convenient, but the code was inflexible, difficult to test and often failed to provide the genuine cross-platform compatibility promised by SOAP.
REST should give rise to more flexible clients that respond to the hypermedia controls returned in responses. They should also be more tolerant of breaking change by only reading the fields that they need. Overall, this provides a more fluid approach to client integration that tends not to break so often.
Swagger undermines this by facilitating a return to automatically generating client applications against a fixed contract. This can also encourage clients to use genuinely RESTful APIs in the “wrong way”, i.e. through pre-generated URI patterns rather than responding to hypermedia controls. The effect is to further undermine REST by creating the kind of rigid, function-orientated clients associated with SOAP or RPC.
Conflating documentation and code
Swagger implementations tend to be used more widely in generating documentation as opposed to client code. This does at least help to ensure that documentation reflects what is in production, but it often comes at the cost of usability.
Automated documentation does depend on the conceit that developers can maintain meaningful documentation as part of their source code. They tend not to. You’re more likely to find boiler plate that is littered with errors. Achieving even the most basic consistency of tone or language can be a challenge, particularly with globally distributed teams.
There can be practical difficulties in coupling public documentation to the end-points exposed by a web application. Many API infrastructures expose end-points via an API gateway that takes responsibility for concerns such as request routing, validation and authorization. This can give rise to inaccurate documentation as behaviours that are defined in a gateway won’t make it to the Swagger output. It may sound old fashioned, but Swagger is no substitute for a good technical writer.
Another issue is that Swagger implementations tend to bloat code bases. Swagger implementations generally try and support a “happy path” where a basic level of support is provided just by adding in the library. For example, the SpringFox implementation for Java Spring just requires a Docket bean which can be used to set a bunch of global defaults and initialise the UI. A similar low-hassle implementation is available for .Net via the Swashbuckle library.
These basic implementations won’t include any of the things that make documentation useful, such as method and field descriptions. These have to be added directly to source code directly, though Swagger implementations generally try to leverage existing code structures where possible. For SpingFox you can use Java annotations to add metadata, while a similar approach is taken for .Net using custom attributes.
The exact approach tends to vary between development environments, but a verbose Swagger implementation inevitably places significant extra burden on the source code. You either have a large and complex configuration file or metadata sprinkled liberally across your methods and models.
If it's RPC that you really want...
Swagger can be a convenient means of outputting API documentation, but the automatic generation of content and client code feel like a poor fit for REST. Adding Swagger to a API implementation can undermine the RESTful aspects, encouraging clients to use it as an RPC API running over HTTP.
If you really want an RPC-style API, along with code generation and contract management, then there are better options than faux REST via Swagger. For example, gRPC is faster, has rich cross-platform support and is based on a structured contract that allows a reasonable degree of backwards compatibility.