How to create a RestFul APIs in Spring Boot | Category API

By | March 14, 2023

RestFul APIs

RestFul APIs are the interface that two computer systems use to communicate and exchange data over the internet using HTTPS ports. The APIs that follow the REST architecture design paradigm are called Rest APIs.

In this article, we will create a category API for our blog application. To learn more about the Category API and its fields and attributes, please refer to the Blog Application-Database Schema.

Steps to create Category API for Blog Application

Step 1- Create Category Class

In this step, we will create a model class for the Category entity. To learn more about the Category class attributes, Please, refer How to design Database Schema. For this, go to Model Package > Right-Click > New > Class > Create Category Class.

Java
package com.paulsofts.blogapplicationservices.model;

import org.springframework.data.annotation.Id;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = "categories")
public class Category {
	
	@Id
	@Column(name = "cateogyId")
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int categoryId;
	//we can define database constraints such as maximum
	//length of categoryTile is 100 and it must not be null
	@Column(name = "categoryTitle", length=100, nullable=false)
	private String categoryTitle;
	@Column(name = "categoryDescription")
	private String categoryDescription;

}

Step 2- Create CategoryDto Class

In this step, we will create the CategoryDto class that is used to transfer data from one layer to another and hides the implementation details of the database layer. For this, go to DTO Package > Right-Click > New > Class > Create CategoryDto Class. We are also using bean validation such as @NotEmpty, @Size, etc. To learn more about bean validation, please refer to Bean Validation – How to validate data using Bean Validation?.

Java
package com.paulsofts.blogapplicationservices.dto;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class CategoryDto {
	
	private int categoryId;
	//we are using the annotations for bean validation
	@NotEmpty
	@Size(min = 4, message = "Category Title must conatins atleast 4 characters")
	private String categoryTitle;
	private String categoryDescription;

}

Step 3- Create CategoryRespository Interface

In this step, we will create the CategoryRepository interface that is used to perform persistence (database) layer operations. For this, go to Repository Package > Right-Click > New > Interface > Create the CategoryRepository Interface. We will need to extends CategoryRepository interface to JpaRepository<T, ID> and need to pass the model class and the wrapper class of the type of ID used in the model class.i.e., JpaRepository<Category, Integer>.

Java
package com.paulsofts.blogapplicationservices.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.paulsofts.blogapplicationservices.model.Category;
/*
 * In the arguments of JpaRepository, we need to pass the model class
 * and the wrapper class of the type of id used in our model class
 * */
public interface CategoryRepository extends JpaRepository<Category, Integer> {

}

Step 4- Create CategoryService Interface

In this step, we will create CategoryService interface. The service classes are used to perform all the business logic on the entity class data which are mapped to JPA. For this, go to Service Package > Right-Click > New > Interface > Create CategoryService Interface.

Java
package com.paulsofts.blogapplicationservices.service;

import java.util.List;

import com.paulsofts.blogapplicationservices.dto.CategoryDto;

public interface CategoryService {
	
	public CategoryDto createCategory(CategoryDto categoryDto);
	public CategoryDto updateCategory(CategoryDto categoryDto, int categoryId);
	public CategoryDto getCategoryById(int categoryId);
	public List<CategoryDto> getAllCategory();
	public void deleteCategory(int categoryId);

}

Step 5- Create CategoryServiceImpl Class

In this step, we will write the corresponding implementation class for the CategoriyService interface. For this, go to Service Package > Right-Click > New > Class > Create CategoryServiceImpl Class, and using this class, implement the CategoryService interface.

Java
package com.paulsofts.blogapplicationservices.service;

import java.util.List;
import java.util.stream.Collectors;

import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.paulsofts.blogapplicationservices.dto.CategoryDto;
import com.paulsofts.blogapplicationservices.model.Category;
import com.paulsofts.blogapplicationservices.repository.CategoryRepository;
import com.paulsofts.blogapplicationservices.utils.ResourceNotFoundException;

@Service
public class CategoryServiceImpl implements CategoryService {
	
	//auto-wiring CategroyRepository to perform persistence operations
	@Autowired
	private CategoryRepository categoryRepository;
	
	//auto-wiring model mapper to map Category to CategoryDto and vice-versa
	@Autowired
	private ModelMapper modelMapper;

	@Override
	public CategoryDto createCategory(CategoryDto categoryDto) {
		
		Category category = this.modelMapper.map(categoryDto, Category.class);
		Category categorySavedToDB = this.categoryRepository.save(category);
		
		//return type of our method is CategoryDto so, we need to
		//map our Category object(.i.e., categorySavedToDB) to CategoryDto object
		return this.modelMapper.map(categorySavedToDB, CategoryDto.class);
	}

	@Override
	public CategoryDto updateCategory(CategoryDto categoryDto, int categoryId) {
		//first we will check whether the Category is present
		//in database or not
		Category categoryToBeUpdated = this.categoryRepository.findById(categoryId).orElseThrow(
				() -> new ResourceNotFoundException("Category", "categoryId", categoryId));
		
		//if the category is present in the database we will update it
		categoryToBeUpdated.setCategoryTitle(categoryDto.getCategoryTitle());
		categoryToBeUpdated.setCategoryDescription(categoryDto.getCategoryDescription());
		
		return this.modelMapper.map(categoryToBeUpdated, CategoryDto.class);
	}

	@Override
	public CategoryDto getCategoryById(int categoryId) {
		Category category = this.categoryRepository.findById(categoryId).orElseThrow(
				() -> new ResourceNotFoundException("Category", "categoryId", categoryId));
		return this.modelMapper.map(category, CategoryDto.class);
	}

	@Override
	public List<CategoryDto> getAllCategory() {
		List<Category> categoryList = this.categoryRepository.findAll();
		
		//we are using streams api to change each category in categoryList
		//to categoryDto and collecting it in categoryDtoList
		List<CategoryDto> categoryDtoList = categoryList.stream().map(category -> 
		this.modelMapper.map(category, CategoryDto.class)).collect(Collectors.toList());
		return categoryDtoList;
	}

	@Override
	public void deleteCategory(int categoryId) {
		Category category = this.categoryRepository.findById(categoryId).orElseThrow(() -> 
		new ResourceNotFoundException("Category", "categoryId", categoryId));
		
		this.categoryRepository.delete(category);
	}

}

Step 6- Create CategoryController Class

In this step, we will create the CategoryController class that is used to handle the incoming requests from the client side. For this, go to Controller Package > Right-Click > New > Class > Create CategoryController Class. We will also use the generic requests and responses. We have used the following GenericRequest<T> and GenericResponse<T> classes.

GenericRequest<CategoryDto> Class

Java – GenericRequest Class
package com.paulsofts.blogapplicationservices.utils;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GenericRequest<T> {
	private T t;
	
}

GenericResponse<CategoryDto> Class

Java – GenericResponse Class
package com.paulsofts.blogapplicationservices.utils;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GenericResponse<T> {
	//T is the generic type which we will take as input
	private T t;
	private String message;
	private String response;

}

Now, we will complete the implementation of our CategoryController class.

Java
package com.paulsofts.blogapplicationservices.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.paulsofts.blogapplicationservices.dto.CategoryDto;
import com.paulsofts.blogapplicationservices.service.CategoryServiceImpl;
import com.paulsofts.blogapplicationservices.utils.GenericRequest;
import com.paulsofts.blogapplicationservices.utils.GenericResponse;

@RestController
@RequestMapping("/api/categories")
public class CategoryController {
	
	@Autowired
	private CategoryServiceImpl categoryServiceImpl;
	
	@PostMapping("/create")
	public ResponseEntity<GenericResponse<CategoryDto>> createCategory(@RequestBody GenericRequest<CategoryDto> request){
		//as we have used the GenericRequest, we need to pull out our model
		//class data using the getT() method of generic class which will be used as request body
		CategoryDto createdCategoyDto = this.categoryServiceImpl.createCategory(request.getT());
		return new ResponseEntity<GenericResponse<CategoryDto>>(
				new GenericResponse<CategoryDto>(createdCategoyDto, "category created", "OK"), HttpStatus.CREATED);
	}
	
	@PostMapping("/update/{categoryId}")
	public ResponseEntity<GenericResponse<CategoryDto>> updateCategory(
			@RequestBody GenericRequest<CategoryDto> request, @PathVariable("categoryId") int categoryId){
		
		CategoryDto updatedCategoryDto = this.categoryServiceImpl.updateCategory(request.getT(), categoryId);
		return new ResponseEntity<GenericResponse<CategoryDto>>(
				new GenericResponse<CategoryDto>(updatedCategoryDto, "category updated", "OK"), HttpStatus.OK);
	}
	
	@GetMapping("/get/{categoryId}")
	public ResponseEntity<GenericResponse<CategoryDto>> getCategoryById(@PathVariable("categoryId") int categoryId){
		CategoryDto categoryDto = this.categoryServiceImpl.getCategoryById(categoryId);
		return new ResponseEntity<GenericResponse<CategoryDto>>(
				new GenericResponse<CategoryDto>(categoryDto, "category", "OK"), HttpStatus.OK);
	}
	
	@GetMapping("/get")
	public ResponseEntity<GenericResponse<List<CategoryDto>>> getAllCategory(){
		List<CategoryDto> categoryDtoList = this.categoryServiceImpl.getAllCategory();
		return new ResponseEntity<GenericResponse<List<CategoryDto>>>(
				new GenericResponse<List<CategoryDto>>(categoryDtoList, "category list", "OK"), HttpStatus.OK);
	}
	
	//as return type of deleteUser() method of service class is void
	//we will be using String as type of our GenericResponse class
	@DeleteMapping("/delete/{categoryId}")
	public ResponseEntity<GenericResponse<String>> deleteCategory(@PathVariable("categoryId") int categoryId){
		this.categoryServiceImpl.deleteCategory(categoryId);
		return new ResponseEntity<GenericResponse<String>>(
				new GenericResponse<String>("categoryId " + categoryId, "category deleted", "OK"), HttpStatus.OK);

	}
}

Step 7- Add Bean Validation

In this step, we will need to add bean validation for our Category APIs. For this, we need to update our CategoryController class. We will add @Valid annotation to the methods where we are taking the CategoryDto Object (Model Class Object). We need to add @Valid annotation to GenericRequest<T> class also as we are pulling the request body (CategoryDto object) from generic type T. To learn more about bean validation please refer, Bean Validation – How to validate data using Bean Validation?.

Java
package com.paulsofts.blogapplicationservices.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.paulsofts.blogapplicationservices.dto.CategoryDto;
import com.paulsofts.blogapplicationservices.service.CategoryServiceImpl;
import com.paulsofts.blogapplicationservices.utils.GenericRequest;
import com.paulsofts.blogapplicationservices.utils.GenericResponse;

import jakarta.validation.Valid;

@RestController
@RequestMapping("/api/categories")
public class CategoryController {
	
	@Autowired
	private CategoryServiceImpl categoryServiceImpl;
	
	//we have added the @Valid annotation for bean validation
	@PostMapping("/create")
	public ResponseEntity<GenericResponse<CategoryDto>> createCategory(@Valid @RequestBody GenericRequest<CategoryDto> request){
		//as we have used the GenericRequest, we need to pull out our model
		//class data using the getT() method of generic class which will be used as request body
		CategoryDto createdCategoyDto = this.categoryServiceImpl.createCategory(request.getT());
		return new ResponseEntity<GenericResponse<CategoryDto>>(
				new GenericResponse<CategoryDto>(createdCategoyDto, "category created", "OK"), HttpStatus.CREATED);
	}
	
	@PostMapping("/update/{categoryId}")
	public ResponseEntity<GenericResponse<CategoryDto>> updateCategory(@Valid
			@RequestBody GenericRequest<CategoryDto> request, @PathVariable("categoryId") int categoryId){
		
		CategoryDto updatedCategoryDto = this.categoryServiceImpl.updateCategory(request.getT(), categoryId);
		return new ResponseEntity<GenericResponse<CategoryDto>>(
				new GenericResponse<CategoryDto>(updatedCategoryDto, "category updated", "OK"), HttpStatus.OK);
	}
	
	@GetMapping("/get/{categoryId}")
	public ResponseEntity<GenericResponse<CategoryDto>> getCategoryById(@PathVariable("categoryId") int categoryId){
		CategoryDto categoryDto = this.categoryServiceImpl.getCategoryById(categoryId);
		return new ResponseEntity<GenericResponse<CategoryDto>>(
				new GenericResponse<CategoryDto>(categoryDto, "category", "OK"), HttpStatus.OK);
	}
	
	@GetMapping("/get")
	public ResponseEntity<GenericResponse<List<CategoryDto>>> getAllCategory(){
		List<CategoryDto> categoryDtoList = this.categoryServiceImpl.getAllCategory();
		return new ResponseEntity<GenericResponse<List<CategoryDto>>>(
				new GenericResponse<List<CategoryDto>>(categoryDtoList, "category list", "OK"), HttpStatus.OK);
	}
	
	//as return type of deleteUser() method of service class is void
	//we will be using String as type of our GenericResponse class
	@DeleteMapping("/delete/{categoryId}")
	public ResponseEntity<GenericResponse<String>> deleteCategory(@PathVariable("categoryId") int categoryId){
		this.categoryServiceImpl.deleteCategory(categoryId);
		return new ResponseEntity<GenericResponse<String>>(
				new GenericResponse<String>("categoryId " + categoryId, "category deleted", "OK"), HttpStatus.OK);

	}
}

Step 8- Test Application

In this step, we will test our complete Category APIs. Once we run our application, we can check the logs for the categories table created in our database.

Eclipse Console Logs
Fig 1- Categories Table Created

We can also check our database schema for the categories table.

MySQL Workbench
Fig 2- Categories Table

Now, first we will try to validate our category APIs, and for that we will send a bad request.

Java Bean Validation
Fig 3- Bean Validation

As in above picture, we can see our bean validation is working fine. Now, we will try to perform CRUD Operations using Category APIs.

Category- Create Request

RestFul APIs - Create Request
Fig 4- Create Request

Category- Update Request

RestFul APIs - Update Request
Fig 5- Update Request

Category- Get Request

RestFul APIs - Get Request
Fig 6- GetById Request

Category- GetAll Request

RestFul APIs - GetAll Request
Fig 7- GetAll Request

Category- Delete Request

RestFul APIs - Delete Request
Fig 8- Delete Request

Leave a Reply

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