Development of RESTful API with Spring Boot in less than 20 minutes

The purpose of this blog is to show how to build restful web services using Java and Spring Boot. I will try to show you some of the best practices you should know when you have to develop that kind of service. Web APIs has become a very important topic. Typically we use a RESTful […]

by Nikola Nenov

March 28, 2017

6 min read

REST api d56810391e9851fade45e40804ad40fd - Development of RESTful API with Spring Boot in less than 20 minutes
The purpose of this blog is to show how to build restful web services using Java and Spring Boot. I will try to show you some of the best practices you should know when you have to develop that kind of service.
Web APIs has become a very important topic. Typically we use a RESTful design for them. The concept of REST is to separate the API structure into logical resources.

There are a few best practices which are for designing a clean RESTful API. Some of them will be used for the purpose of this blog.

Using nouns for url naming convention is strongly preferred
Use only plural nouns – /cars instead of /car
Sub – resources for relations
/api/cars – returns a list of all cars
/api/cars/4 – returns a car with id=4
Provide filtering and sorting for collections
Filtering – /cars?car_engine=V12 – returns a list of cars with that engine
Sorting – /cars?sort=horsepower
Use HTTP status codes
Version your API – /v1/api/

Martin Fowler blog was used as a reference.

Service overview and requirements:

Store new car object into data base
Change parameters for current existing car
Delete existing car
Retrieve a car from data base by id
Retrieve a list of all cars stored in data base

The source code can be found on my GitHub profile.

Work flow
First I will list the anchor points for developing this tutorial and then all of them will be explained in same order.

Create DB
Add project dependencies
Add a main method which will be used for project execution
Create DAO
Add Service layer
Short explanation and example for adding resource for the implementation of our REST endpoint

Let’s start !

1. Setting up the data base

CREATE TABLE muscle_cars (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
car_brand VARCHAR(40) NOT NULL,
car_model VARCHAR(40) NOT NULL,
horsepower INT NOT NULL,
car_engine VARCHAR(50) NOT NULL,
)

insert into muscle_cars (car_brand, car_model, horsepower, car_engine)
values
('Plymouth', 'GTX 440 Six Pack', 375, 'V8'),
('Ford', 'Mustang 428 Super Cobra Jet', 375, 'V8'),
('Plymouth', 'Superbird 426 Hemi', 	425, 'V6'),
('Dodge', 'Challenger R/T 426 Hemi', 450, 'V12')

2. Create project

2.1 Maven
These are the dependencies which are need for this tuorial. The complete code can be found in GitHub repository.

<dependencies>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
	</dependency>

	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-test</artifactId>
	    <scope>test</scope>
	</dependency>

	<dependency>
	    <groupId>com.jayway.jsonpath</groupId>
	    <artifactId>json-path</artifactId>
	    <scope>test</scope>
	</dependency>

	<dependency>
	    <groupId>commons-dbcp</groupId>
	    <artifactId>commons-dbcp</artifactId>
	</dependency>

	<dependency>
	    <groupId>mysql</groupId>
	    <artifactId>mysql-connector-java</artifactId>
	</dependency>

	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-jdbc</artifactId>
	</dependency>

</dependencies>

2.2 Add main method

The execution of the project will start when fire up this main method.

@SpringBootApplication( scanBasePackages = "com.scar")
@Import({DataSourceConfig.class})
public class CarsApplication {
	public static void main(String[] args) {
	SpringApplication.run(CarsApplication.class, args);
	}
}

The @SpringBootApplication annotation is equivalent to using @Configuration, @EnableAutoConfiguration, @ComponentScan with their default attributes.

3. Setting up the DAO

For the purpose of this tutorial I will use the abstract class which Spring provides – JdbcDaoSupport. By extending JdbcDaoSupport , set the datasource and Jdbctemplate in your class is no longer required, you just need to inject the correct datasource into MuscleCarDaoImpl. And you can get JdbcTemplate by using a getJdbcTemplate() method.

@Repository
public class MuscleCarDaoImpl extends JdbcDaoSupport implements MuscleCarDao {

	@Autowired
	public MuscleCarDaoImpl(JdbcTemplate jdbcTemplate, DataSource dataSource) {
		jdbcTemplate = new JdbcTemplate(dataSource);
		setJdbcTemplate(jdbcTemplate);
	}

	@Override
	public MuscleCar getCarFromList(int id) {
		String sql = "select * from muscle_cars where id = ?";
		Object[] args = new Object[] { id };
		return getJdbcTemplate().queryForObject(sql, args, new MuscleCarRowMapper());
	}

	@Override
	public void removeCarFromList(int id) {
		String sql = "delete from muscle_cars where id = ?";
		Object[] args = new Object[] { id };
		getJdbcTemplate().update(sql, args);
	}

    @Override
    public void addCarToList(MuscleCar muscleCar) {
        String sql = "insert into muscle_cars (car_brand, car_model, horsepower, car_engine) values (?, ?, ?, ?)";
        Object[] args = new Object[] {muscleCar.getCarBrand(), muscleCar.getCarModel(), muscleCar.getHorsepower(), muscleCar.getCarEngine()};
        getJdbcTemplate().update(sql, args);
    }

    @Override
    public void updateCarFromList(int id, MuscleCar muscleCar) {
        String sql = "update muscle_cars set car_brand = ?, car_model = ?, horsepower = ?, car_engine = ? where id =?";
        Object[] args = new Object[] {muscleCar.getCarBrand(), muscleCar.getCarModel(), muscleCar.getHorsepower(), muscleCar.getCarEngine(), id};
        getJdbcTemplate().update(sql, args);
    }

    @Override
    public List<Map<String, Object>> listAllCars() {
        String sql = "select * from muscle_cars";
        return getJdbcTemplate().queryForList(sql);
    }

}

4. Adding a Service layer

The service will do some basic verifications for the data which we will retrieve. If a layer like this is need in a project which you are working on the verifications must be more detailed.

@Service
public class MuscleCarService {

	@Autowired
	private MuscleCarDao muscleCarDao;

	public MuscleCar getCar(int id) throws ValidateException {
		if (id <= 0) {
			throw new ValidateException("ID can not be 0 or <0");
		}
		return muscleCarDao.getCarFromList(id);
	}

	public void removeCarFromList(int id) throws ValidateException {
		if (id <= 0) {
			throw new ValidateException("ID can not be 0 or < 0 or this id do not exist");
		}
		 muscleCarDao.removeCarFromList(id);
	}

	public List<Map<String, Object>> listAllCars() {

		List<Map<String, Object>> result = muscleCarDao.listAllCars();
			if (!result.isEmpty()) {
				return result;
			} else {
				return null;
			}
	}

	public void addCarToList(MuscleCar muscleCar) throws MuscleCarException {
		if (muscleCar == null) {
			throw new MuscleCarException("The passed object can not be null.");
		}
		muscleCarDao.addCarToList(muscleCar);
	}

	public void updateCarFromList(int id, MuscleCar muscleCar) {
		if ( id <= 0 && muscleCar == null) {
			throw new IllegalArgumentException("The passed object can not be null.");
		}
		muscleCarDao.updateCarFromList(id, muscleCar);
	}

}

5. Adding a Resource

Next up, we have the implementation of our REST endpoint. We will use this class to map our service to incoming HTTP requests.
The @RestController is a convenience annotation between @Controller and @ResponseBody and marks this class as a web component discovered during class path scanning. The @RequestMapping annotation at the class level defines the base path mapping used for any other @RequestMapping annotations in this class. In this case all endpoints will start with path – /api/muscle.
The @RequestMapping annotation has many options and with this tutorial we are using small part.

value=”/url/{id}” used in conjunction with @PathVariable(“id”) int id maps the {id} part of the url path to the given method argument.
method = RequestMethod.GET/POST/PUT/DELETE define the HTTP method that is accepted
Method parameters annotated with @RequestBody will be populated with the incoming request JSON data.

@RestController
@RequestMapping(value = "/v1/api")
public class MuscleCarResource {

	@Autowired
	private MuscleCarService muscleCarService;

	@RequestMapping(value = "/cars", method = RequestMethod.GET)
	public ResponseEntity<List<Map<String, Object>>> listAllCars() {

		try {
			List<Map<String, Object>> result = muscleCarService.listAllCars();
			return ResponseEntity.status(HttpStatus.OK).body(result);
		} catch (IllegalStateException e) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
		}
	}

	@RequestMapping(value = "/cars/{id}", method = RequestMethod.GET)
	public ResponseEntity<MuscleCar> getMuscleCar(@PathVariable("id") int id) {

		try {
			MuscleCar muscleCar = muscleCarService.getCar(id);
			if (muscleCar != null) {
				return ResponseEntity.status(HttpStatus.OK).body(muscleCar);
			} else {
				return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
			}

		} catch (ValidateException e) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
		}

	}

	@RequestMapping(value = "/cars/delete/{id}", method = RequestMethod.DELETE)
	public String deleteMuscleCar(@PathVariable("id") int id) {

		try {
			muscleCarService.removeCarFromList(id);
			return  String.valueOf(ResponseEntity.status(HttpStatus.OK));
		} catch (ValidateException e) {
			return String.valueOf(ResponseEntity.status(HttpStatus.BAD_REQUEST));
		}

	}

	@RequestMapping(value = "/cars/add", method = RequestMethod.POST)
	public ResponseEntity<Void> addCarToList( @RequestBody MuscleCar muscleCar) {

		try {
			muscleCarService.addCarToList(muscleCar);
			return ResponseEntity.status(HttpStatus.OK).build();
		} catch (MuscleCarException e) {
			e.printStackTrace();
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
		}
	}

	@RequestMapping(value = "/cars/update/{id}", method = RequestMethod.PUT)
	public ResponseEntity<Void> updateCar(@PathVariable("id") int id, @RequestBody MuscleCar muscleCar) {

		try {
			muscleCarService.updateCarFromList(id, muscleCar);
			return ResponseEntity.status(HttpStatus.OK).build();
		} catch(IllegalStateException e ) {
			return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
		}
	}

}

That’s it. Run the application and see the results at https://localhost:8080

For easy testing can be used a tool like SoapUI or some browser plug-in like Postman.

Categories

Full Stack Developer at Dreamix