Swagger REST API

Using JAX-RS API under JSR-339, it is easy to create REST services using excellent Java platform. But developers do not have any option using JSR-339 to generate any documentation on all these APIs so other developers could quickly understand how to use them. For sure, it would be just awesome to generate verbose and easy to follow documentation from source code, and but the help is coming in a form of Swagger.

Essentially, Swagger does a simple but very powerful thing: with a bit of additional annotations it generates the REST API descriptions such as HTTP methods, path / query / form parameters, responses, HTTP error codes etc) and even provides a simple web UI to play with REST calls to your APIs.

You may also like to explore Spring Boot REST API Documentation with Swagger 2 and  Documenting Spring REST API with Swagger 2


The Swagger specification is a powerful definition format to describe RESTful APIs. The Swagger specification creates a RESTful interface for easily developing and consuming an API by effectively mapping all the resources and operations associated with it. It’s easy-to-learn, language agnostic, and both human and machine readable.

Swagger is a powerful open source framework backed by a large ecosystem of tools that helps you design, build, document, and consume your RESTful APIs.

Swagger supports seamless integration with JAX-RS services, with just couple of additional annotations required on top of existing ones.

Example
Step 1. Create a web based maven project (rest-swagger) in Eclipse
Step 2. Modify the pom.xml file as shown below

<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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.roytuts</groupId>
    <artifactId>rest-swagger</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>rest-swagger Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- core swagger -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-jaxrs</artifactId>
            <version>1.5.10</version>
        </dependency>
        <!-- Supports documentation of Jersey-based file uploads -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-jersey-jaxrs</artifactId>
            <version>1.5.10</version>
        </dependency>
        <!-- Supports documentation of Jersey2-based file uploads -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-jersey2-jaxrs</artifactId>
            <version>1.5.10</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>rest-swagger</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.webcohesion.enunciate</groupId>
                <artifactId>enunciate-maven-plugin</artifactId>
                <version>2.7.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>docs</goal>
                        </goals>
                        <configuration>
                            <!-- the directory where to put the docs -->
                            <docsDir>${project.build.directory}/docs</docsDir>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

In the above pom.xml file you see that there are dependencies for swagger APIs and also you notice the plugin enunciate-maven-plugin which is used to generate the REST API documentation. You will see later how the REST API documentation looks like. When you look at the documentation, you will find that you don’t need to look at the REST service source code to understand how to use the REST service but the documentation explains every details of the REST service.

Step 3. Create below model class

package com.roytuts.rest.swagger.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "User", description = "User resource representation")
public class User {
    @ApiModelProperty(value = "User's name", required = true)
    private String name;
    @ApiModelProperty(value = "User's email", required = true)
    private String email;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
}

Here in the model class above, you see I have used annotations @ApiModel(swagger model) and @ApiModelProperty(model property). These annotations are required to be processed by swagger API because you want to appear every details of your written code into the documentation.

Step 4. Create below dummy repository class. In real application you are going to use some persistence storage.

package com.roytuts.rest.swagger.repository;
import java.util.HashMap;
import java.util.Map;
import com.roytuts.rest.swagger.model.User;
public enum UserRepository {
    _instance;
    private Map<String, User> userMap = new HashMap<>();
    private UserRepository() {
        // default user
        User user = new User();
        user.setEmail("contact@roytuts.com");
        user.setName("Soumitra Roy");
        userMap.put(user.getEmail(), user);
    }
    public Map<String, User> getUserRepository() {
        return userMap;
    }
}

I have put one default User into the repository instead of keeping it blank.

Step 5. Create below exception classes for handing exceptions in REST service.

package com.roytuts.rest.swagger.exceptions;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
public class UserNotFoundException extends WebApplicationException {
    private static final long serialVersionUID = 6505273692205817545L;
    public UserNotFoundException(final String email) {
        super(Response.status(Status.NOT_FOUND).build());
    }
}

The above class is used when HTTP response code is 404.

package com.roytuts.rest.swagger.exceptions;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
public class UserAlreadyExistsException extends WebApplicationException {
    private static final long serialVersionUID = -7185802088093678972L;
    public UserAlreadyExistsException(final String email) {
        super(Response.status(Status.CONFLICT).build());
    }
}

The above class is used when HTTP response code is 409.

Step 6. Create below REST service resource class

package com.roytuts.rest.swagger.resources;
import java.util.ArrayList;
import java.util.Collection;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import com.roytuts.rest.swagger.exceptions.UserAlreadyExistsException;
import com.roytuts.rest.swagger.exceptions.UserNotFoundException;
import com.roytuts.rest.swagger.model.User;
import com.roytuts.rest.swagger.repository.UserRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
@Path("/user")
@Api(value = "/user")
public class UserRestService {
    @Context
    private UriInfo uriInfo;
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "List of all users", notes = "List of all users using pagination", response = User.class, responseContainer = "List")
    public Collection<User> getAllUsers() {
        Collection<User> users = new ArrayList<>();
        users.addAll(UserRepository._instance.getUserRepository().values());
        return users;
    }
    @GET
    @Path("/{email}")
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Find a user by email", notes = "Find a user by email", response = User.class)
    @ApiResponses({ @ApiResponse(code = 404, message = "User with such email address does not exist") })
    public User getUserByEmail(
            @ApiParam(value = "Email address to look up for", required = true) @PathParam("email") final String email) {
        User user = UserRepository._instance.getUserRepository().get(email);
        return user;
    }
    @POST
    @Path("/addUser")
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Create a new user", notes = "Create a new user")
    @ApiResponses({ @ApiResponse(code = 201, message = "User created successfully"),
            @ApiResponse(code = 409, message = "User with such email address exists already") })
    public Response addUser(@ApiParam(value = "User object in JSON string", required = true) final User user) {
        if (UserRepository._instance.getUserRepository().containsKey(user.getEmail())) {
            throw new UserAlreadyExistsException(user.getEmail());
        }
        Response response = Response.created(uriInfo.getAbsolutePath()).build();
        UserRepository._instance.getUserRepository().put(user.getEmail(), user);
        return response;
    }
    @PUT
    @Path("/{oldEmail}")
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Update a user", notes = "Update a user", response = User.class)
    @ApiResponses({ @ApiResponse(code = 404, message = "User with such email address does not exist") })
    public User updateUser(
            @ApiParam(value = "Email address to look for", required = true) @PathParam("oldEmail") final String oldEmail,
            @ApiParam(value = "User object in JSON string", required = true) final User user) {
        if (UserRepository._instance.getUserRepository().containsKey(oldEmail)) {
            user.setEmail(user.getEmail());
            user.setName(user.getName());
            UserRepository._instance.getUserRepository().remove(oldEmail);
            UserRepository._instance.getUserRepository().put(user.getEmail(), user);
            return UserRepository._instance.getUserRepository().get(user.getEmail());
        }
        throw new UserNotFoundException(oldEmail);
    }
    @DELETE
    @Path("/{email}")
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "Delete a user", notes = "Delete a user")
    @ApiResponses({ @ApiResponse(code = 404, message = "User with such email address does not exist") })
    public Response deleteUser(
            @ApiParam(value = "Email address to look for", required = true) @PathParam("email") final String email) {
        if (UserRepository._instance.getUserRepository().containsKey(email)) {
            UserRepository._instance.getUserRepository().remove(email);
            return Response.ok().build();
        }
        throw new UserNotFoundException(email);
    }
}

In the above class I have annotated the class and methods with standard REST annotations as well as swagger annotations(@Api*) to be processed by swagger API put every details into the API documentation.

Step 7. Now finally put the below code into deployment descriptor file – web.xml.

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>rest-cookie</display-name>
    <servlet>
        <servlet-name>REST</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.roytuts.rest.swagger.resources</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Map /rest/* to Jersey -->
    <servlet-mapping>
        <servlet-name>REST</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Step 8. If you just deploy the application(rest-swagger) into Tomcat server and test using REST client for all operations one-by-one then test results will look something similar to below outputs

When you call getAllUsers method


swagger rest api

When you call getUserByEmail


swagger rest api

When you call addUser method


swagger rest api

When you verify whether the User has been added or not; so call getAllUsers method


swagger rest api

When you call updateUser method

swagger rest api

When you call deleteUser method

swagger rest api

So you have just finished testing the REST services and now you know how to invoke all these methods defined in the UserRestService class by looking at the source code.

Now you will see how to know every details of the service without looking at the source code of the REST service operations.

Step 1. Go to the project root folder using command prompt
Step 2. Run command mvn clean. Again run command mvn compile.
Step 3. If everything was fine in the above step, then go to the target directory of your projects root folder. You will find one docs directory and inside this docs directory another directory called apidocs directory. Open it and double click on index.html file to open the REST service API documentation for every details about the service operations.

You will something looks similar to below image and you can click on the different operations to know more details on it.


swagger rest api

Thanks for reading.

Leave a Reply

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