Spring Rest API with Swagger – Integration and configuration

Nowadays, exposed APIs are finally getting the attention they deserve and companies are starting to recognize their strategic value. However, working with 3rd party APIs can be really tedious work, especially when these APIs are not maintained, ill designed or missing any documentation. That’s why I decided to look around for ways to provide fellow programmers and other team members with proper documentation when it comes to integration. One way to go is to use WADL, which is a standard specifically designed to describe HTTP based web applications (like REST web services). However there are few drawback when using WADL that made me look elsewhere for solutions how to properly document and expose API documentation.

Swagger

Another way might be to go with Swagger. Swagger is both specification and framework implementation that supports full life cycle of RESTful web services development. The specification itself is language-agnostic, which might come in handy in heterogeneous environment. Swagger also comes with Swagger UI module which allows both programmers and other team members to meaningfully interact with APIs and gives them a way to work with it while providing access to the documentation.

Spring with Jersey example

Not long ago, I came across an article describing Swagger specification and I was pretty intrigued to give it a try. At that time I was working on a sweet little microservice so I had an ideal testing ground to try it out. Based on that I prepared a short example about how to use Swagger in your application, when you are using Spring framework and Jersey. Example code models simplified REST API for a subset of possible APIs in a shop application scenario.

Note: Import declarations were omitted from all Java code samples.

Jersey servlet

Before we get down to introducing Swagger to our code, lets take a moment and explore our example a little. First of all, lets look at web.xml. There is plain old web.xml with few simple declarations and mappings in code sample below. Nothing special here, just a bunch of configuration.

<web-app id="SpringWithSwagger" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>Spring Jersey Swagger Example</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>jersey-serlvet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.jakubstas.swagger.SpringWithSwagger</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>jersey-serlvet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

Endpoint

Second thing we are going to need is the endpoint that defines our REST service – for example employee endpoint for listing current employees. Once again, there is nothing extraordinary, only a few exposed methods providing core API functionality.

package com.jakubstas.swagger.rest;

@Path("/employees")
public class EmployeeEndpoint {

    private List<Employee> employees = new ArrayList<Employee>();

    {
        final Employee employee = new Employee();
        employee.setEmployeeNumber(1);
        employee.setFirstName("Jakub");
        employee.setSurname("Stas");

        employees.add(employee);
    }

    @OPTIONS
    public Response getProductsOptions() {
        final String header = HttpHeaders.ALLOW;
        final String value = Joiner.on(", ").join(RequestMethod.GET, RequestMethod.OPTIONS).toString();

        return Response.noContent().header(header, value).build();
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getEmployees() {
        return Response.ok(employees).build();
    }
}

Swagger dependencies

First thing we need to do is to include all required Swagger dependencies in our pom.xml as shown below (lucky for us it’s only a single dependency).

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    ...
    <properties>
        ...
        <swagger-version>1.3.8</swagger-version>
        ...
    </properties>
    ...
    <dependencies>    
        ...
        <!-- Swagger -->
        <dependency>
            <groupId>com.wordnik</groupId>
            <artifactId>swagger-jersey2-jaxrs_2.10</artifactId>
            <version>${swagger-version}</version>
        </dependency>
        ...
    </dependencies>
</project>

Swagger configuration

Now, lets take a look how Swagger integrates into our example. As with any introduction of a new dependency in your project, you should be concerned with how invasive and costly this process will be. The only effected places will be your REST endpoints, Spring configuration and some transfer objects (given you choose to include them) as you will see in following code samples. This means that there is no configuration needed in web.xml for Swagger to work with your Spring application, meaning it’s rather non-invasive in this way and remains constrained within APIs realm.

You need three basic properties for Swagger to work:

  • API version
    • Provides the version of the application API
  • base path
    • The root URL serving the API
  • resource package
    • Defines package where to look for Swagger annotations

Since API maintenance is primarily responsibility of analysts and programmers, I like to keep this configuration in a separate property file called swagger.properties. This way it is not mixed with application configuration and is less likely to be modified by accident. Following snippet depicts such a configuration file.

swagger.apiVersion=1.0
swagger.basePath=http://[hostname/ip address]:[port]/SpringWithSwagger/rest
swagger.resourcePackage=com.jakubstas.swagger.rest

For a second part of configuration I created a configuration bean making use of previously mentioned properties. Using Spring’s @PostConstruct annotation providing bean life-cycle hook, we are able to instantiate and set certain attributes that Swagger requires, but is not able to get (in current version at least).

package com.jakubstas.swagger.rest.config;

/**
 * Configuration bean to set up Swagger.
 */
@Component
public class SwaggerConfiguration {

    @Value("${swagger.resourcePackage}")
    private String resourcePackage;

    @Value("${swagger.basePath}")
    private String basePath;

    @Value("${swagger.apiVersion}")
    private String apiVersion;

    @PostConstruct
    public void init() {
        final ReflectiveJaxrsScanner scanner = new ReflectiveJaxrsScanner();
        scanner.setResourcePackage(resourcePackage);

        ScannerFactory.setScanner(scanner);
        ClassReaders.setReader(new DefaultJaxrsApiReader());

        final SwaggerConfig config = ConfigFactory.config();
        config.setApiVersion(apiVersion);
        config.setBasePath(basePath);
    }

    public String getResourcePackage() {
        return resourcePackage;
    }

    public void setResourcePackage(String resourcePackage) {
        this.resourcePackage = resourcePackage;
    }

    public String getBasePath() {
        return basePath;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    public String getApiVersion() {
        return apiVersion;
    }

    public void setApiVersion(String apiVersion) {
        this.apiVersion = apiVersion;
    }
}

Last step is to declare following three Swagger beans: ApiListingResourceJSONApiDeclarationProvider and ResourceListingProvider.

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:component-scan base-package="com.jakubstas.swagger" />
    <context:property-placeholder location="classpath:swagger.properties" />

    <bean class="com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON" />
    <bean class="com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider" />
    <bean class="com.wordnik.swagger.jaxrs.listing.ResourceListingProvider" />
</beans>

Swagger is now configured and you can check whether your setup is working properly. Just enter the URL from your basePath variable followed by /api-docs into your browser and check the result. You should see an output similar to following snippet I received after accessing http://[hostname]:[port]/SpringWithSwagger/rest/api-docs/ in my example.

{"apiVersion":"1.0","swaggerVersion":"1.2"}

What is next?

If you followed all steps you should now have working setup to start with an API documentation. I will showcase how to describe APIs using Swagger annotations in my next article called Spring Rest API with Swagger – Creating documentation. The code used in this micro series is published on GitHub and provides examples for all discussed features and tools. Please enjoy! 🙂

Update 06.04.2015: Since some users reported issues with updating the dependencies used in my example project I decided to create a second project for Spring 4 to provide distinct solutions for both Spring 3 and Spring 4.

25 thoughts on “Spring Rest API with Swagger – Integration and configuration

  1. Very nice tool for documentation and use of rest APIs. I wonder what would be the case with cloud micro-services, specially, with defining a basePath as this property would be discoverable.

    1. Hi Haytham, thanks for your comment. There are several points you need to take into consideration when you want to secure your Swagger documentation depending on your specific needs and also your application / environment like the basePath, Swagger UI, … Since Swagger UI is just a collection of static resources you can modify it to your likeness, deploy it separately and apply security mechanisms to allow only ‘trusted users’ to access it. I am not sure what you need and I don’t feel that confident in this area myself (this might be the topic of one of my future posts though 🙂 ) so I would suggest you to contact guys behind Swagger (Google user group: https://groups.google.com/forum/#!forum/swagger-swaggersocket). They are really open to questions and quick to respond so this would be a nice starting point.

    1. Hi Manish, sorry for the delay in my answer, but I was on holidays and away from my laptop. However, I though a bit about your problem and it seems to be a problem of URLs and configuration. After some playing around with my example I was able to reproduce this error by damaging my Swagger UI config. Having found this, I would suggest checking if your json is created the way it should and on the URL you expect it to. If there is no issue there, go into Swagger UI files and look for index.html. Check if the javascript code creating SwaggerUi object has the proper URL configured to consume the json documentation.

      1. Jakub,

        The JSON is also not getting created on the URL. I checked my Index.html & it has proper URL. If I configure all resources in web.xml without spring then I am getting proper JSON but with spring config I get the error I mentioned. Also what do you mean by damaging Swagger UI config?

        1. Hm … seems like some of the Swagger stuff doesn’t get picked up. Have you tried debugging your version of SwaggerConfiguration from my example? Especially the init method code? I would also suggest to crank up the logging level detail to see what is going on under the hood (both Springs and Swaggers logs show quite a lot of useful info). If this won’t help try to create new project with simple endpoint and endpoint related stuff and try this greenfield setup for yourself. This may show you where the difference between my example and your app is.

          I might have confused you with the expression ‘damaging’. All I did was modifying the configuration so it doesn’t work (by supplying invalid URL).

          1. Jakub,

            I debugged my version of SwaggerConfiguration & there are no issue. Can I mail you my config to have a look? Meanwhile I will try another sample project as you suggested & let you know the details.

          2. Sure, I can take a look at it but I can’t guarantee that I will be able to help you with it.

  2. Jakub,

    If possible can you send mail me your complete project so that I can have a look & then see where I am going wrong?

  3. Hi Manish,

    I also received same problem but its resolved by clean & install the maven project again.

  4. Jakub – Thank you for such a nice project. I’m developing your same code to use all current latest maven dependencies, when I use spring latest dependency like 4.1.5.RELEASE or 4.0.9.RELEASE it’s giving me following error:

    Unexpected exception parsing XML document from class path resource [beans.xml]; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.support.DefaultListableBeanFactory.getDependencyComparator()Ljava/util/Comparator;

    FYI – I’m using updated versions of all maven dependencies. With you used old version’s of dependencies it’s working absolutely fine.

    I’ve created issue in GITHUB as well.

    1) Also I see you’ve use 3rd party Sawgger library, off-course it’s uses by you for Jersey2 library. Could you please try to update project to use latest dependencies? I personally think, no sense in using the old libraries.

    com.wordnik
    swagger-jersey2-jaxrs_2.10
    1.3.12

    2) However, I see spring itself uses the swagger dependecy. so could you please create separate project to use it? (Fyi – I’m also giving attempt to create it, but not succeeded so far)

    com.mangofactory
    swagger-springmvc
    1.0.2

    Could you help? Many Thanks once again

  5. Many many thanks to you Jakub. I really appreciate your work & explanation style. It’s really gonna be very useful information that you are sharing with us. I just follow your steps and just one shot I ran your the project without any errors. Thank you very much from bottom of my heart, you really saved my lots of time because I gone though many articles to configure the same thing but every project has some dependency related issues or some internal issues.

    Regards
    Vipul

  6. Hi Jakub,
    The tutorial is looking nice. So there is no need of all the buzz on github about swagger-ui and swagger-core???

    1. Hi Rajesh, thank you. I don’t know what you mean by the buzz. Can you clarify what you want to find out?

      1. I just mean to whether we need that swagger-ui and swagger-core from github or we don’t need that.

        1. Well, that depends on what you want to do. If you want to generate Swagger json and then parse/process it somehow you will need swagger and/or its integration with the framework you use. If you also want the UI presented in my posts you will need Swagger UI and you will need to configure it to consume whatever is produced be Swagger.

          1. Hi Jakub,

            Thanks for the reply, I am working on your example and stuck here
            Can you please tell me what is the following class used for. Please provide coding detail of the following class(SpringWithSwagger).

            com.jakubstas.swagger.SpringWithSwagger

  7. Nice tutorial, but for some reasons the API section of the generated JSON does not appear … any idea?

    1. That is just too little information to work with. I would suggest you crank up logging level to see what is really going on and see if Swagger gets picked up and initialized. You should be able to take it from there.

Leave a Reply

Your email address will not be published. Required fields are marked *