Document HAL "_links" from Spring Hateoas (with swagger)?

JR Utily picture JR Utily · Jun 24, 2014 · Viewed 7.4k times · Source

I have a REST service I want to document for my client developing team.

So I added some Links from Spring-Hateoas to my resources API, and plugged into it swagger-springmvc @Api... annotations to document everything and make a good API reference for my Angular team to be able to understand my REST service.

The problem is that swagger is unable to discover what links are possible, and just give me a big array of Links without saying their possible values.

Here is a (simple) example. Swagger detects :

Model Schema
CollectionListResource {
    collections (array[CollectionResource]): All available collections,
    links (array[Link]): Relations for next actions
}
CollectionResource {
    collectionId (string): Collection Unique Id,
    name (string): Human readable collection name,
    links (array[Link]): Relations for next actions
}
Link {
    rel (string, optional),
    templated (boolean, optional),
    href (string, optional)
} 

And I get in fact in HAL :

 {"collections":
    [{"collectionId":"5370a206b399c65f05a7c59e",
      "name":"default",
       "_links":{ [
           "self":{
              "href":"http://localhost:9080/collections/5370a206b399c65f05a7c59e"
            },

           "delete":{
              "href":"http://localhost:9080/collections/5370a206b399c65f05a7c59e"
            }
        ]}
       }, ...]}   

I've tried to extend Link and ResourceSupport to have annoted version of them but this lead me to nowhere.

Is there a way/tool I could use to generate a good API doc telling that a self relation is to get the content, and a delete relation is to delete the collection ?

I liked swagger for its good UI, but I don't mind changing my documentation tool if its help having the doc really complete.

I could eventually think of changing spring-hateoas for another link generator, but I'm not sure there is a better tool available right now.

Answer

Dilip Krishnan picture Dilip Krishnan · Nov 17, 2014

Swagger-UI as such is not hypermedia aware; or atleast its limited in that it can ONLY navigate from top level apis to api listings. That has not changed much in v2.0 of the spec either, with the notable addition of linking to external documents for out of band documentation.

What you need is a hybrid of the HAL browser and the swagger-ui. As you rightly pointed out, there is a semantic gap between the word "delete" and what it means in the context of a collection resource. HAL uses a combination of curies and optionally a profile document ALPS to bridge this gap. Curies are nothing but namespaced versions of linked relations so that they dont collide with other domains. Restful Web APIs is a great resource to learn more about these ideas and how to design media types. The spring data rest project also has a great example of how to achieve that.

  • One of the ways that I think would work is to adjust swagger specification to support an operations oriented view rather than api listing oriented view, not really possible in the the timeframe that you're possibly working with.
  • Use existing RFC's like rfc5023 to have a shared understanding of what "edit"ing a resource means.
  • Lastly if none of the standard link relationships express the intent of the action, define your own application specific semantics that provide documentation for these application specific link relationships. That way clients of your service will have a shared understanding of these relations within the context of your application.

Below is an example that demonstrates and combines these approaches.

{"collections":
[{"collectionId":"5370a206b399c65f05a7c59e",
  "name":"default",
  "curies": [
       {
          "name": "sw",
          "href": "http://swagger.io/rels/{rel}",
          "templated": true
       },
       {
          "name": "iana",
          "href": "http://tools.ietf.org/html/rfc5023",
          "templated": false
       },
       {
          "name": "col",
          "href": "http://localhost:9080/collections/{rel}",
          "templated": false
       }
   ],
   "_links":{ [
        "self":{
          "href":"http://localhost:9080/collections/5370a206b399c65f05a7c59e"
        },
        "sw:operation":{
          "href":"http://localhost:9080/api-docs/collections#delete"
        },
        "sw:operation":{
          "href":"http://localhost:9080/api-docs/collections#search"
        },
        "sw:operation":{
          "href":"http://localhost:9080/api-docs/collections#update"
        },
        "iana:edit":{
          "href":"http://localhost:9080/collections/5370a206b399c65f05a7c59e"
        },
        "col:delete": {
          "href":"http://localhost:9080/collections/5370a206b399c65f05a7c59e"
        }
    ]}
   }, ...]} 

So from most generic to most specific, the solution (in that order) is

  • The iana qualified links have a specific meaning, in this case the "edit" has a very specific meaning that every restful client can implement. This is a generialized link type.
  • The sw qualified link relations have a special meaning as well, it implies the href deep links to operations in the swagger api documentation.
  • The col qualified links are application specific links that only your application knows about.

I know this doesn't answer your question precisely, and the tools in this space are still evolving. Hope this helps.

DISCLAIMER: I'm one of the core maintainers of springfox which is a spring integration solution that makes it easy to provide swagger service descriptions. So any feedback on how you'd like it to solve this problem is very welcome!