Spring Boot + REST Jersey Part 1


In this post we will explain in a simple way the basic configuration of Spring Boot with Jersey, this is the first in a series of posts that will be explaining more advanced topics regarding the previously mentioned technologies.

1.- Configuration:

Configuring Spring Boot is a very simple task, you just need to inherit from the spring boot base project, include the dependencies of the spring modules and configure the plugins to be used in the project.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.0.RELEASE</version>
</parent>

When we inherit from the spring boot parent project, we are inheriting all the versions defined for each component so you don’t need specify the versions in each dependency.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
    </dependency>
</dependencies>

As you can see, you only need specify the dependencies but not the versions, they are all specified in the spring-boot-starter-parent.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.- Creating a main class:

Once we have completed our maven settings the next step is to create a main class for our application, this class will be responsible for the initialisation of the container and the project; the default container for the application is Jetty but as it is indicated by the dependencies in our pom.xml file, we will be using Tomcat.

/**
 *
 */
package com.raidentrance;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author raidentrance
 *
 */
@SpringBootApplication
public class SprinBootSampleApplication  {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(SprinBootSampleApplication.class, args);
    }

}

One of the most common questions at this point is “Why are we using a main method if we are creating a web application?” The answer is quite simple, the application is not going to generate a war file but a fat jar, it is known as fat jar because besides the application itself, it includes an embedded container such as a Jetty or Tomcat one. In future posts we will explain how to use this in a production environment.

3.- Writing the REST web service.

To create a REST web service with spring you just have to use the annotations from the Jersey project and mark the classes with @Component to add it to the Spring context.

/**
 *
 */
package com.raidentrance.resource;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.springframework.stereotype.Component;

/**
 * @author raidentrance
 *
 */

@Component
@Path("/users")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {

    @GET
    public String getUsers() {
     return "{\"username\":\"raidentrance\"}";
    }
}

The following lines are intended to describe the purpose of each annotation used in the present example:

  • @Component: Used to include the class UserResource in the Spring Context.
  • @Path(“users”): Used to define an endpoint where the service is going to be accessed, it is possible to put another annotation at method level to specify more complex endpoints.
  • @Produces(MediaType.APPLICATION_JSON) / @Consumes(MediaType.APPLICATION_JSON): These annotations specify that the endpoints registered on that class will consume and produce JSON files, for this reason the method getUsers() returns a JSON formatted string, it is possible to put these annotations in a method level to modify its behaviour.

4.- Registering the service:

Once we have the service and spring boot has been configured, the missing step is to register the service in Jersey, for this you have to create a class where all the services are going to be registered.

/**
 *
 */
package com.raidentrance.config;

import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

import com.raidentrance.resource.UserResource;

/**
 * @author raidentrance
 *
 */
@Component
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
         register(UserResource.class);
    }
}

This class shouldn’t be invoked from anywhere in the application, just by being registered using the @Component annotation and inheriting from ResourceConfig Spring will know which configuration to use to register the services.

5.- Executing and testing.

To test the application, the only thing we need to do is to execute  SpringBootSampleApplication class as a desktop application, or using its jar file with the command:

 java -jar target/jar-name.jar

Output:

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.0.RELEASE)

2016-09-02 11:23:28.005 INFO 20231 --- [ restartedMain] c.r.SprinBootSampleApplication : Starting SprinBootSampleApplication on m-C02RV1WXG8WP.local with PID 20231 (/Users/maagapi/Documents/Github/spring-boot-example/target/classes started by maagapi in /Users/maagapi/Documents/Github/spring-boot-example)
2016-09-02 11:23:28.008 INFO 20231 --- [ restartedMain] c.r.SprinBootSampleApplication : No active profile set, falling back to default profiles: default
2016-09-02 11:23:28.064 INFO 20231 --- [ restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@53294542: startup date [Fri Sep 02 11:23:28 CDT 2016]; root of context hierarchy
2016-09-02 11:23:28.646 INFO 20231 --- [ restartedMain] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2016-09-02 11:23:29.000 INFO 20231 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2016-09-02 11:23:29.012 INFO 20231 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service Tomcat
2016-09-02 11:23:29.013 INFO 20231 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.4
2016-09-02 11:23:29.075 INFO 20231 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2016-09-02 11:23:29.075 INFO 20231 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1013 ms
2016-09-02 11:23:29.260 INFO 20231 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-09-02 11:23:29.261 INFO 20231 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2016-09-02 11:23:29.261 INFO 20231 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'com.raidentrance.config.JerseyConfig' to [/*]
2016-09-02 11:23:29.441 INFO 20231 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2016-09-02 11:23:29.469 INFO 20231 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2016-09-02 11:23:29.510 INFO 20231 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-09-02 11:23:29.513 INFO 20231 --- [ restartedMain] c.r.SprinBootSampleApplication : Started SprinBootSampleApplication in 2.206 seconds (JVM running for 2.542)

To test the code you should access http://localhost:8080/users in your web browser.

In the following sections we will explain how to add logs, configure spring security, spring data, error handling and deployments to production environments.

If you want to learn more about web services we recommend the following books:

You can find the complete code for this project in the following link:

https://github.com/raidentrance/spring-boot-example

Author: Alejandro Agapito Bautista

Twitter: @raidentrance

Contact: raidentrance@gmail.com

Translated by: Oscar Camacho – melchior01

Contact: adamfirstangel@hotmail.com

Java design patterns, first part. Creational design patterns (Factory method, Singleton and Builder pattern)


Design patterns were developed as solutions for common problems that every developer faces. Design patterns are classified based on their main purpose; a design pattern may be any of the following types:

  •  Creational patterns
  •  Structural patterns
  •  Behavioral patterns

In this post, we are primarily focused on creational patterns and their implementation in Java.

Factory method

Factory method is a design pattern that centralizes the creation of objects that follow a hierarchical structure; this pattern is composed by the following elements:

  •  A factory class: In this case, as an example, we create the class SpritesFactory.
  • The product to be built: For this example, the Java objects built by the application.

Our example will be build using following hierarchical structure:
factory

Modelling our application

/*
*/
package com.raidentrance.model.stripes;

import java.awt.Graphics;

/**
 * @author raidentrance
 *
 */
public abstract class Sprite {
	protected int x;
	protected int y;
	protected int width;
	protected int heigth;

	public abstract void draw(Graphics g);

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeigth() {
		return heigth;
	}

	public void setHeigth(int heigth) {
		this.heigth = heigth;
	}
}

The Sprites class is an abstract class where we define common methods for all of the Sprites, such as: getX(), setX(int x), getY(), setY(int y), getWidth(),setWidth(int width), getHeigth() and  setHeigth(); as well as an abstract method called draw(Graphics g) that is different for each one of the implementations.

package com.raidentrance.model.stripes;

import java.awt.Graphics;

/**
 * @author raidentrance
 *
 */

public class Hero extends Sprite {

	@Override
	public void draw(Graphics g) {
		System.out.println("Drawing a hero");
	}
}
/*
**
*/
package com.raidentrance.model.stripes;

import java.awt.Graphics;

/**
 * @author raidentrance
 **/
public class BadBoy extends Sprite {
	@Override
	public void draw(Graphics g) {
		System.out.println("Drawing a bad boy");
	}
}

Hero.java and BadBoy.java are Sprite.java subclasses. We use the Factory method to instantiate these subclasses.

Creating the object factory class

The next step is to create the class responsible of following the Factory method design pattern. This class creates the instances within the application.

/**
*
*/
package com.raidentrance.factory;

import com.google.common.base.Preconditions;
import com.raidentrance.model.stripes.BadBoy;
import com.raidentrance.model.stripes.Hero;
import com.raidentrance.model.stripes.Sprite;

/**
 * @author raidentrance
 *
 */

public class SpritesFactory {
	public static enum SpriteType {
		HERO, BAD_BOY
	}

	public static Sprite createStripe(SpriteType type) {
		Preconditions.checkNotNull(type);
		switch (type) {
		case HERO:
			return new Hero();
		case BAD_BOY:
			return new BadBoy();
		}
		throw new IllegalArgumentException();
	}
}

There are two key points to highlight in the SpriteFactory class. Firstly, it contains an enumeration that defines all of the possible types of objects than can be built. Secondly, the method createStripes(StripeType type) receives an SpriteType as argument and depending on this argument it returns the corresponding instance.

Putting all together

Finally, we create a class to gather the classes previously described and observe how the objects are instantiated.

/**
*
*/
package com.raidentrance.factory;

import com.raidentrance.model.stripes.Sprite;

/**
 * @author raidentrance
 *
 */

public class Sample {
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Sprite sprite = SpritesFactory.createStripe(SpritesFactory.SpriteType.HERO);
		sprite.draw(null);
		Sprite sprite2 = SpritesFactory.createStripe(SpritesFactory.SpriteType.BAD_BOY);
		sprite2.draw(null);
	}
}

Output

Drawing a hero
Drawing a bad boy
Singleton pattern

Singleton pattern is used to define a class that contains a single instance and provides a single point of access to it.

In the following example, we create a Singleton class to define the set up parameters of an application.

Defining a class for error handling

/**
*
*/
package com.raidentrance.commons;

/**
 * @author raidentrance
 *
 */

public enum ErrorResult {

	EMPTRY_RESULT("%s cannot be null"), INVALID_ARGUMENT("%s is not a valid type");
	private String message;

	private ErrorResult(String message) {
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}
/**
*
*/

package com.raidentrance.model;

/**
 * @author raidentrance
 *
 */

public class BussinessException extends Exception {

	private static final long serialVersionUID = 1788240335231977221L;

	public BussinessException(String msg) {
		super(msg);
	}
}

Defining the Singleton class

/**
*
*/

package com.raidentrance.singleton;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import com.google.common.base.Preconditions;
import com.raidentrance.commons.ErrorResult;
import com.raidentrance.model.BussinessException;

/**
 * @author raidentrance
 *
 */

public class Settings {
	private String host;
	private int port;
	private String context;
	private String username;
	private String password;
	private static Settings instance = new Settings();

	private Settings() {
	}

	public static Settings getInstance() {
		return instance;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getContext() {
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUrl() {
		Preconditions.checkNotNull(host,
				new BussinessException(String.format(ErrorResult.EMPTRY_RESULT.getMessage(), "host")));
		return host + ":" + port + context;
	}

	public String getCredentials() {
		return "Basic ".concat(Base64.getEncoder()
				.encodeToString(username.concat(":").concat(password).getBytes(StandardCharsets.UTF_8)));
	}
}

Points to highlight:

  1. Private constructor: It is fundamental to create a private constructor, by this means, the Singleton class is the only one capable to instantiate itself.
  2. Private static instance: It is necessary to define the instance as private and static, in order to make this variable accessible only inside the Singleton class.
  3. getInstance() method: This is the only point of access to the instance.

Putting all together.

/**
 *
 */
package com.raidentrance.singleton;

/**
 * @author raidentrance
 *
 */
public class Sample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Settings settings = Settings.getInstance();
		System.out.println("Setting default settings.");
		settings.setHost("http://localhost");
		settings.setPort(8080);
		settings.setContext("/rest");
		settings.setUsername("user");
		settings.setPassword("SecretPassword");

		System.out.println(Settings.getInstance().getUrl());
		System.out.println(Settings.getInstance().getCredentials());

		System.out.println("Updating settings.");
		settings = Settings.getInstance();
		settings.setHost("http://192.168.5.1");
		settings.setUsername("prodUser");
		settings.setPassword("UltraSecretPassword");

		System.out.println(Settings.getInstance().getUrl());
		System.out.println(Settings.getInstance().getCredentials());
	}
}

Output

Setting default settings.
http://localhost:8080/rest
Basic dXNlcjpTZWNyZXRQYXNzd29yZA==
Updating settings.
http://72.51.22.13:8080/rest
Basic cHJvZFVzZXI6VWx0cmFTZWNyZXRQYXNzd29yZA==

As shown in this example, it doesn’t really matter how many times you invoke the getInstance() method, this will always return the same instance.

Builder Pattern

This design pattern is used to build complex object step by step.

Modelling our application

/**
 *
 */
package com.raidentrance.model;

import java.util.Date;

/**
 * @author raidentrance
 *
 */
public class Person {
	private String name;
	private Date birthDate;
	private int age;

	public Person() {
	}

	public Person(String name, Date birthDate, int age) {
		super();
		this.name = name;
		this.birthDate = birthDate;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getBirthDate() {
		return birthDate;
	}

	public void setBirthDate(Date birthDate) {
		this.birthDate = birthDate;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", birthDate=" + birthDate + ", age=" + age + "]";
	}
}

 

/**
 *
 */
package com.raidentrance.model;

/**
 * @author raidentrance
 *
 */
public class Book {
    private String isbn;
    private String title;
    private String author;
    private double price;

    public Book() {
    }

    public Book(String isbn, String title, String author, double price) {
        super();
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.price = price;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book [isbn=" + isbn + ", title=" + title + ", author=" + author + ", price=" + price + "]";
    }

}

Helper class to calculate the age

The following class calculates a person’s age based on her birthday.

/**
 *
 */
package com.raidentrance.commons;

import java.util.Calendar;
import java.util.Date;

/**
 * @author raidentrance
 *
 */
public class TimeHelper {
    public static int getAge(Date birthDate) {
        Calendar dob = Calendar.getInstance();
        dob.setTime(birthDate);
        Calendar today = Calendar.getInstance();
        int age = today.get(Calendar.YEAR) - dob.get(Calendar.YEAR);
        if (today.get(Calendar.MONTH) < dob.get(Calendar.MONTH)) {
            age--;
        } else if (today.get(Calendar.MONTH) == dob.get(Calendar.MONTH)
                && today.get(Calendar.DAY_OF_MONTH) < dob.get(Calendar.DAY_OF_MONTH)) {
            age--;
        }
        return age;
    }
}

Creating an abstract builder

An abstract builder class allows you to follow the same structure in all of the builders that you might want to develop. In this example, we create two builders, the first one to create Person objects and the second one to create Book objects, both of them extend AbstractBuilder.

/**
 *
 */
package com.raidentrance.builder.commons;

/**
 * @author raidentrance
 *
 */
public abstract class AbstractBuilder<T> {
    protected T instance;

    public AbstractBuilder(T instance) {
        this.instance = instance;
    }

    public T build() {
        inject();
        validate();
        return instance;
    }

    public abstract void validate();

    public abstract void inject();
}

Points to highlight on the AbstractBuilder class:

  1. This class has a generic instance that will contain the object that we want to build.
  2. Its constructor receives this instance as argument.
  3. It contains three important methods:
    • inject(): This method is used to generate values based on the attributes that it has.
    • validate(): This method validates that all of the prerequisites to correctly build an object are accomplished.
    • build(): This method calls the inject() and validate() method, and returns the instance.

Creating builders

PersonBuilder.java

/**
 *
 */
package com.raidentrance.builder;

import java.util.Date;

import com.google.common.base.Preconditions;
import com.raidentrance.builder.commons.AbstractBuilder;
import com.raidentrance.commons.ErrorResult;
import com.raidentrance.commons.TimeHelper;
import com.raidentrance.model.BussinessException;
import com.raidentrance.model.Person;

/**
 * @author raidentrance
 *
 */
public class PersonBuilder extends AbstractBuilder<Person> {

    public PersonBuilder() {
        super(new Person());
    }

    public PersonBuilder setName(String name) {
        instance.setName(name);
        return this;
    }

    public PersonBuilder setBirthDate(Date birthDate) {
        instance.setBirthDate(birthDate);
        return this;
    }

    @Override
    public void validate() {
        Preconditions.checkNotNull(instance.getName(),
                new BussinessException(String.format(ErrorResult.EMPTRY_RESULT.getMessage(), "Name")));
        Preconditions.checkNotNull(instance.getBirthDate(),
                new BussinessException(String.format(ErrorResult.EMPTRY_RESULT.getMessage(), "Birth date")));
        Preconditions.checkNotNull(instance.getAge(),
                new BussinessException(String.format(ErrorResult.EMPTRY_RESULT.getMessage(), "Age")));
    }

    @Override
    public void inject() {
        Preconditions.checkNotNull(instance.getBirthDate(),
                new BussinessException(String.format(ErrorResult.EMPTRY_RESULT.getMessage(), "Birth date")));
        instance.setAge(TimeHelper.getAge(instance.getBirthDate()));
    }
}

Points to highlight on the PersonBuilder class:

  1. The PersonBuilder constructor sends a new Person instance as argument.
  2. Each setter method returns a PersonBuilder instance.
  3. The inject() method is used to generate the age based on a birthday.
  4. The validate() method validates that the variables are not null.
  5. The build() method is already defined on the AbstractBuilder class and it returns a complete and correctly formed instance.

BookBuilder.java

package com.raidentrance.builder;

import com.google.common.base.Preconditions;
import com.raidentrance.builder.commons.AbstractBuilder;
import com.raidentrance.commons.ErrorResult;
import com.raidentrance.model.Book;
import com.raidentrance.model.BussinessException;

/**
 * @author raidentrance
 *
 */
public class BookBuilder extends AbstractBuilder<Book> {

    public BookBuilder() {
        super(new Book());
    }

    public BookBuilder setIsbn(String isbn) {
        instance.setIsbn(isbn);
        return this;
    }

    public BookBuilder setTitle(String title) {
        instance.setTitle(title);
        return this;
    }

    public BookBuilder setAuthor(String author) {
        instance.setAuthor(author);
        return this;
    }

    public BookBuilder setPrice(double price) {
        instance.setPrice(price);
        return this;
    }

    @Override
    public void validate() {
        Preconditions.checkNotNull(instance.getIsbn(),
                new BussinessException(String.format(ErrorResult.EMPTRY_RESULT.getMessage(), "Isbn")));
    }

    @Override
    public void inject() {

    }
}

You can find the code previously explained in the following link:  https://github.com/raidentrance/design-patterns

Author: Alejandro Agapito Bautista

Twitter: @raidentrance

e-mail:raidentrance@gmail.com

Translated By: Oscar Camacho – melchior01

Contact: adamfirstangel@hotmail.com

Best practices for better RESTful Web Services


Nowadays the use of REST web services is essential in the development of web applications, APIs creation, etc. One of the most common mistakes on the design of this kind of applications comes when a developer is not aware of the best practices that should be followed, and, as a consequence the application turns to be messy, hard to use and understand.

On this post we will mention some of the best practices used to create REST web services.

To be ALWAYS STATELESS:

The first rule when managing REST web services is to design them 100% STATELESS, meaning this that each request done by some client should be independent from the previous one.

Stateless web services allows the application to be easily scalable avoiding the migration of user sessions from one cluster to the new ones, you just need to put a load balancer and a new instance of the application and you can be sure that every single request will be processed (as long as this is a correct request!).

Use nouns, not verbs:

Not to do:

Method: GET     Endpoint: /getUsers/

Description: Returns a list of users.

You should always use nouns instead of verbs, when a developer use verbs the endpoint turns to be redundant since it is already defined on the HTTP method.

The correct way to define the previous endpoint is the following:

Method: GET     Endpoint: /users/

Description: Returns a list of users.

This example is using an HTTP GET method and the /users/ endpoint which sounds logic and its purpose is easy to understand.

If you need to get just one user, the correct way to handle this is the following:

Method: GET     Endpoint: /users/123

Description: Returns a user with the specified ID.

As you can see, there is no need for verbs on any endpoint since the /users/{id} endpoint is easy to understand.

Some examples of common mistakes are:

/getUsers  /updateUser      /deleteUser

Use the correct HTTP method

A common mistake on many REST APIs is that most of their web services are usually GET and POST. Here you have a list of the available HTTP methods, their use and some common responses to those

  • GET: Returns one or many resources according to the consumed endpoint, the most common response is a JSON with the requested object or objects.
  • POST: Creates a new resource, a good practice is to return a link to the created resource. For more details you can review the following section about HATEOAS.
  • PUT: Updates an already created resource, a good practice is to return a link to the created resource. For more details you can review the following section about HATEOAS.
  • PATCH: Partially updates an existent resource, a good practice is to return a link to the created resource. For more details you can review the following section about HATEOAS.
  • DELETE: Deletes a resource, a good practice is to return a 204 HTTP status (Not content) to confirm that the resource has been deleted successfully.

On the next paragraphs we will explain on detail the most common HTTP status and when to use those.

Allow HTTP methods to be overridden

Some HTTP clients can only work using GET and POST requests; this is why it is necessary to allow method overrides (X-HTTP-Method-Override, allowing the override from a POST to a PUT, PATCH or DELETE request. This is only used to change the behavior of the POST method; GET should never modify any resource from the server.

Correct use of HTTP methods

If your web services are returning an HTTP 200 status for every kind of request (correct or incorrect), and equally sending HTTP 500 status on every error, you will be facing several issues trying to identify the piece that is failing in your server due to the poor information given by those HTTP status. On the next lines you will find the uses of each range of HTTP status:

  • 1xx (100 – 199): Informational status.
  • 2xx (200-299): Successful status, some responses of this range are: 200 (OK) meaning successful request or 201 (Not content) meaning that a resource has been successfully deleted.
  • 3xx (300-399): Redirection, this response indicates that there is a missing step to finish the request so a redirection will be tried to get it.
  • 4xx (400-499): Client side error. It is common to return always an HTTP 400 status for different kind of errors. The correct way to use it is to identify if the error was due to something in the client or server sides. For example, an HTTP 404 response occurs when a client is trying to retrieve a non-existent resource. An HTTP 403 status occurs when a client does not have access to retrieve a specific resource.
  • 5xx (500-599): Server side error. Similarly to the previous status, the developer should be aware of the origin of an error if this error comes from the client or the server. These kinds of errors are related to something wrong on the server for example a bad connection with a DB.

If for every request your REST web services are returning a correct status, the client will be able to understand and handle any error in the best way. Here you have an example of an endpoint with each of its possible responses.

Method: GET       Endpoint: /users/123

Description: Returns a user with the specified ID.

Responses:

  • HTTP 200: The user exists and the client has access to this resource. It will return a JSON file with the requestd data.
  • HTTP 404: The requested user does not exist. This will return a JSON file with an error message.
  • HTTP 403: The client has been authenticated but it is not authorized to get the specified resource. A JDSON file with the error message will be returned.

Use HATEOAS (Hypermedia as Engine of Application State)

Use HATEOS provides a great user experience when working with an API allowing it to understand each web service without using any kind of documentation. You can review an example of this tool on the following post: Spring Boot + REST Jersey (Using Spring Hateoas and DTO) part 3.

Use only SSL for all your requests

Many users use to connect their application using public networks on streets that can be easily eavesdropped by anyone, due to cases like this it is required for all the requests to be processed through an SSL connection.

API Documentation

Documenting a REST API is not as complex and tedious as it was a few years ago, nowadays you can find different tools that are used to generate documentation emulate requests and also generate code snippets for the clients to be used in different programming languages. Good recommendations of this kind of tools are https://apiary.io/ or https://swagger.io/.

Be careful when to use REST

Nowadays many people use RESTful web services for everything, but not all the cases require a RESTful web service, if the application has it’s own database (And this application is the only one that uses the database)  you can just create a connection to the database and that is all.

Pagination

There are endpoints that can return a big quantity of records which sometimes is hard to handle due to the available resources on the client side and also in the server side. The following code snippet illustrates how to use pagination inside a GET request:

GET /tweets?offset=20&limit=10

Versioning your API

You should never run an API without a version since there will be moments where it is going to be necessary to modify the behavior of some web service, for this, it is better to have different versions so you can be sure you are using the recently updated code. There are many ways to do versioning and it is very controversial in the industry here the following are some available options to do versioning in an api:

  • URL Versioning:  
    HTTP GET: /application/api/v1
  • Adding a custom header in the request:  
    HTTP GET: /application/api/v1  api-version: 1
  • Media type versioning:
    HTTP GET :  /application/api/v1
    Accept: application/raidentrance.api.v2+json
  • Domain versioning
    HTTP GET :  apiv1.geeks-mexico.com/application/api/v1
    Accept: application/raidentrance.api.v2+json
  • Request parameter versioning
    HTTP GET : /application/api/v1/users/?version=1

Authentication

As we mentioned before, a REST API must be Stateless, meaning that every request should be sent to an Authentication mechanism such as OAuth 2 (you can find more information here). This kind of requests has to be managed using an SSL connection.

Always use a lightweight framework

Nowadays REST web services are widely used in different architectures of micro services where just simple tasks are going to be executed. This is why it is better to work with lightweight environments giving a simple way to build and code applications.

The practices presented in this post are language-independent since any REST API can follow them.

If you want to learn more about web services we recommend the following books:

Author: Alejandro Agapito Bautista

Twitter: @raidentrance

Contact:raidentrance@gmail.com

Translated By: Oscar Camacho – melchior01

Contact: adamfirstangel@hotmail.com