DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Overcoming Swagger Annotation Overload by Switching to JSON

Overcoming Swagger Annotation Overload by Switching to JSON

After implementing our API using Spring Boot, we integrated Swagger using the slick SpringFox library. But all was not well.

Joe Wolf user avatar by
Joe Wolf
·
May. 15, 16 · Opinion
Like (6)
Save
Tweet
Share
35.63K Views

Join the DZone community and get the full member experience.

Join For Free

When creating an API with accompanying Swagger documentation, two general paths can be taken:

  1. Build First: Implement the API --> add Swagger annotations--> generate the UI and clients from the annotations
  2. Design First: Design the API spec in Swagger YAML or JSON --> generate the UI, clients, and server stubs from the spec --> implement the server stubs

On my recent project, we had embarked down the "Build First" path. After implementing the API using the mighty Spring Boot, we integrated Swagger using the slick SpringFox library, as widely demonstrated in numerous blog posts.

But all was not well.

Our API had a number of endpoints that supported over a dozen query parameters, some of which were common across all endpoints (e.g. sort, limit, offset, etc.). These parameters were not enumerated in the controller methods' arguments lists; instead, we accepted a single WebRequest argument that we processed in a non-controller class. This resulted in a number of code smells:

  1. The Swagger annotations were separate from much of the code they were documenting. Our controller classes contained the Swagger annotations describing the parameters, but it was our WebRequest processor class that defined what parameters were actually handled.
  2. Due to the commonality of our query parameters, there was a large degree of copy-paste between controller methods' annotations. The description of the sort parameter, for example, existed in half a dozen @ApiImplicitParam annotations scattered across multiple controller classes.
  3. There were more lines of annotations than actual implementation code in the controller classes. They looked like case studies from annotatiomania.com.
  4. The code looked aesthetically unpleasing. IDEs struggle to nicely auto-format big blocks of nested annotations containing multi-line element values.
  5. The Java language proved to be a clunky medium for writing documentation in. What's worse than concatenating a bunch of lengthy, markup-filled Java String literals? Concatenating a bunch of lengthy, markup-filled Java String literals within an annotation element.

I was anxious to clean up the annotation vomit, but given our implementation was essentially complete, was it too late to change direction and hop on a "Design First," specification-driven path instead? In the words of Robert Plant, a man of renowned swagger:

Yes there are two paths you can go by, but in the long run, there's still time to change the road you're on.

Finding the API Spec

The first step was obtaining the API's specification as a single, Swagger 2.0 JSON file. By inspecting the standard Spring Boot /mappings endpoint, I found where SpringFox hosted the spec: http://localhost:8080/v2/api-docs. This URL is also mentioned in the SpringFox documentation (I have a "Try First" rather than "Read First" personality).

Armed with the JSON, I was able to edit API documentation in YAML using Swagger Editor, which I found to be more pleasant than tweaking Strings within blocks of Java annotations.

Cleaning Up

Next, I removed all of my Swagger annotations, including @EnableSwagger2 from my main class, and theio.springfox:springfox-swagger2 dependency.

The code looked beautiful again, but without SpringFox in play, the /v2/api-docs/ endpoint no longer existed. I re-created it by copying the JSON spec to src/main/resources/swagger.json and building a small controller class to expose it:

@RestController
public class SwaggerController {

    @RequestMapping(method = GET, path = "/v2/api-docs", produces = APPLICATION_JSON_VALUE)
    public Resource apiDocs() {
        return new ClassPathResource("swagger.json");
    }
}    

Re-Enabling the Swagger UI

In addition to the core SpringFox dependency, our project relied on io.springfox:springfox-swagger-ui to generate the Swagger UI. We were able to retain this dependency and allow the UI to continue working by recreating three endpoints that the SpringFox UI's index.html needs for self-configuration: /configuration/ui, /configuration/security, and/swagger-resources.

@RequestMapping(method = GET, path = "/configuration/ui", produces = APPLICATION_JSON_VALUE)
public Object uiConfig() {
    return ImmutableList.of(ImmutableMap.of(
            "docExpansion", "none",
            "apisSorter", "alpha",
            "defaultModelRendering", "schema",
            "jsonEditor", Boolean.FALSE,
            "showRequestHeaders", Boolean.TRUE));
}

@RequestMapping(method = GET, path = "/configuration/security", produces = APPLICATION_JSON_VALUE)
public Object securityConfig() {
    return ImmutableList.of(ImmutableMap.of(
            "apiKeyVehicle", "header",
            "scopeSeparator", ",",
            "apiKeyName", "api_key"));
}

@RequestMapping(method = GET, path = "/swagger-resources", produces = APPLICATION_JSON_VALUE)
public Object resources() {
    return ImmutableList.of(ImmutableMap.of(
            "name", "default",
            "location", "/v2/api-docs", // should match the endpoint exposing Swagger JSON
            "swaggerVersion", "2.0"));
}

This example controller just hardcodes the default endpoint values; a more thorough implementation would allow external configurability and perhaps use something more type-safe than collections of Strings. For even more configurability for the UI (e.g. custom look and feel, corporate header, etc.), you could embed Swagger UI directly into the application. I recommend using webjars, which is the approach SpringFox UI takes.

Wrapping It Up

Despite taking the "Build First" approach, we were able to revert to a "Design First"-like state relatively painlessly. The resulting (largely) annotation-free code was much easier on the eyes and the single definition file worked much better for documentation purposes. Perhaps in the future, I'll build an API with a design more suited for Swagger annotations or use a true design first approach. But for this particular API, changing course was the right call.

JSON Annotation

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 10 Most Popular Frameworks for Building RESTful APIs
  • Spring Boot vs Eclipse MicroProfile: Resident Set Size (RSS) and Time to First Request (TFR) Comparative
  • Scaling Your Testing Efforts With Cloud-Based Testing Tools
  • How To Handle Secrets in Docker

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: