In the last part, we explored how to store, retrieve, modify and delete data easily using Spring Boot. This part will take over from where we left off and make use of the things we learned to create a REST API.
Link to part 3
Part 5 is now available: Here
Table of Contents:
- Introduction to REST APIs and Spring Magic
- Property Entity and Repository
- Service Layer
- REST API Basics
- Inserting Data
- Fetching Data
- Query and URL Parameters
- Updating and Deleting
5. Jackson: Handling JSON
6. More Spring REST Magic
Airbnb REST API:
Let’s build a CRUD API for a property lease/lent site like Airbnb.
Start by creating a new spring-boot project just like we have done before from https://start.spring.io/.
Now lets create our Property Entity and a Repository implementation for it.
Here is how the Property Entity looks like
import javax.persistence.*;
@Entity
@Table(name = "properties")
public class Property {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "description")
private String description;
@Column(name = "price")
private double price;
// Other fields such as location, amenities, etc.
// Constructors, getters, and setters
}…
At this point you might be thinking, “Really? I have to go through the whole process of creating a repository and defining the CRUD methods for it?”
Hold on! There is an easier way!
Spring provides an interface which you can extend to get all the basic CRUD operations like findAll, findById, Create, Update and Delete for free which minimal coding.
Simple create a PropertyRepository interface and extend JpaRepository Class, passing in your class name (‘Property’ in this case) and your primary key type (‘Long’ in this case).
package com.example.Airbnb.Repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.Airbnb.Model.Property;
@Repository
public interface PropertyRepository extends JpaRepository<Property, Long> {
// spring provides all the basic crud operations. No Coding!
}
Don’t worry you can add custom methods and still customize the Repository as you like.
And just like that, our Repository and Entity implementations are done.
Service Layer:
This is how our architecture looks now.

Notice the Service Layer. We didn’t go over this in the last part but having a service layer is actually a really good practice in the real world.
The main purpose of a Service is to handle all the business logic. It can combine operations of multiple Repositories together into a single method.
Let’s create a service layer for our application using the @Service annotation.
package com.example.Airbnb.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.Airbnb.Model.Property;
import com.example.Airbnb.Repository.PropertyRepository;
import java.util.List;
import java.util.Optional;
@Service
public class PropertyService {
private final PropertyRepository propertyRepository;
@Autowired
public PropertyService(PropertyRepository propertyRepository) {
this.propertyRepository = propertyRepository;
}
public List<Property> getAllProperties() {
return propertyRepository.findAll();
}
public Optional<Property> getPropertyById(Long id) {
return propertyRepository.findById(id);
}
public Property saveProperty(Property property) {
return propertyRepository.save(property);
}
public void deleteProperty(Long id) {
propertyRepository.deleteById(id);
}
// You can add more methods based on your business logic and requirements
}
We just Autowired the repository inside the Service and used it in the service methods.
Use @Transactional annotation on the Service methods to combine multiple Repository operations as a single atomic transaction.REST API:

An REST API basically exposes URLs/endpoints for the front-end part of our application to display and modify data.
REST, which stands for Representational State Transfer, is an architectural style for designing networked applications. It was introduced by Roy Fielding in his doctoral dissertation in 2000. REST is not a protocol or a standard, but rather a set of architectural principles and constraints that guide the design of distributed systems.
REST uses JSON for data transfer. We can create a REST API in Spring Boot using the @RestController mapping.
package com.example.Airbnb.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class PropertyController {
}
Make sure to include the Spring-Web dependency in pom.xml for this to work.
<! — Add this to your dependencies section →
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Under the hood, spring scans our files for any classes annotated with @RestController and does all the heavy lifting for us. One of those things is converting JSON to Java Objects and vice versa. It does this using a library called as Jackson which is included in the Spring-Web module.
We will learn more about Jackson a little later.
Inserting Data:
Let’s add a route to insert data using the @PostMapping annotation. Post here corresponds to the HTTP POST method.
package com.example.Airbnb.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.example.Airbnb.Model.Property;
import com.example.Airbnb.Service.PropertyService;
import java.util.List;
@RestController
public class PropertyController {
private final PropertyService propertyService;
@Autowired
public PropertyController(PropertyService propertyService) {
this.propertyService = propertyService;
}
@PostMapping("properties")
public ResponseEntity<Property> createProperty(@RequestBody Property property) {
Property savedProperty = propertyService.saveProperty(property);
return new ResponseEntity<>(savedProperty, HttpStatus.CREATED);
}
}Here, we use the Service we created earlier to insert the data into our database.
Now, if you start the server using ./mvnw spring-boot:start
and make a POST request to http://localhost:8080/properties with the necessary data, it will be added to your database.
Example CURL request:
curl -X POST -H "Content-Type: application/json" -d '{
"name": "Cozy Apartment",
"description": "A comfortable apartment with great views",
"price": 100.00
}' http://localhost:8080/propertiesJackson automatically binded the JSON data into the Property object parameter of createProperty method.
Fetching Data:
Now, let’s retrieve whatever we inserted into the database using the @GetMapping annotation.
@GetMapping("/properties")
public ResponseEntity<List<Property>> getAllProperties() {
List<Property> properties = propertyService.getAllProperties();
return new ResponseEntity<>(properties, HttpStatus.OK);
}This will get all the Properities we inserted in a List and return the result to us. Again, Jackson played the role of converting the List of Properties into a JSON.
Example CURL request:
curl -X GET http://localhost:8080/api/propertiesQuery and URL Parameters:
Suppose you want to get only a particular property by ID. You can pass it in as a parameter in the URL and get it using the @pathVariable annotation.
@GetMapping("/{id}")
public ResponseEntity<Property> getPropertyById(@PathVariable Long id) {
return propertyService.getPropertyById(id)
.map(property -> new ResponseEntity<>(property, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}Example CURL request:
curl -X GET http://localhost:8080/api/properties/1Or what if we wanted to find the properties whose prices’ are between a range? We can make use of query parameters and retrieve it using @RequestParam annotation in our code.
@GetMapping("/search")
public ResponseEntity<List<Property>> getPropertiesByPriceRange(
@RequestParam(name = "minPrice", required = false) Double minPrice,
@RequestParam(name = "maxPrice", required = false) Double maxPrice) {
if (minPrice == null && maxPrice == null) {
// If both minPrice and maxPrice are not provided, return all properties
return new ResponseEntity<>(propertyService.getAllProperties(), HttpStatus.OK);
} else {
// If either or both minPrice and maxPrice are provided, filter properties by price range
List<Property> filteredProperties = propertyService.getPropertiesByPriceRange(minPrice, maxPrice);
return new ResponseEntity<>(filteredProperties, HttpStatus.OK);
}
}Example CURL request:
curl -X GET "http://localhost:8080/api/properties/search?minPrice=50.0&maxPrice=150.0"Updating and Deleting:
I am not gonna go into depth, but as you can guess we do similar routes for Updating and Deleting the data as well.
@DeleteMapping("properties/{id}")
public ResponseEntity<Void> deleteProperty(@PathVariable Long id) {
propertyService.deleteProperty(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@PutMapping("/updateName")
public ResponseEntity<Property> updatePropertyName(
@RequestParam(name = "id") Long id,
@RequestParam(name = "name") String name) {
// Retrieve the existing property
Optional<Property> optionalProperty = propertyService.getPropertyById(id);
if (optionalProperty.isPresent()) {
Property existingProperty = optionalProperty.get();
// Update the name of the property
existingProperty.setName(name);
// Save the updated property
Property updatedProperty = propertyService.saveProperty(existingProperty);
return new ResponseEntity<>(updatedProperty, HttpStatus.OK);
} else {
// Property not found
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}During updating, we first check if the property exists and only then we update its value.
You can find the complete code here https://github.com/BenMeehan/airbnb-medium
A Little Bit About Jackson:
Remember the we did not have to do any Java to JSON conversion or vice-versa. This was all taken care by a library called Jackson included in the Spring-Web dependency.
Jackson is widely used in Java applications, especially in the context of building web services or APIs (Application Programming Interfaces) where data needs to be exchanged in a format like JSON. It simplifies the process of handling data in a format that is both human-readable and machine-readable.
In order for Jackson to work properly, make sure all your entity properties have Getter and Setter methods defined. Jackson makes use of these methods for the convertions.
If you don’t have Getters and Setters defined, that field might be omitted or you might face an error.
If you want to generate Getters and Setter automatically, have a look at Project Lombok
More Spring REST Magic:
Spring Boot can automatically generate all the basic mappings we did in the PropertyController class as well.
Simply, add the below dependency to your pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>Spring will automatically scan your project for Entities and generate all the REST routes for them using the Services you have created. The route names will be the Entity name in plural with first letter in lower case.
For example, if your entity is called Property then the route will be
/properties
Now, You can remove your Controller file entirely and you will still be able to access the routes and do all the CRUD operations we did before.
With Spring Data REST, you can expose your Spring Data repositories as RESTful APIs without having to write explicit controllers. It automatically generates CRUD (Create, Read, Update, Delete) operations for your entities.
Spring Data REST embraces the HATEOAS principle, meaning that the API responses include hypermedia links that guide clients on how to interact with the API. Clients can navigate the API by following links, reducing the need for them to have prior knowledge of the API structure. You can read more about it here.
Spring Data REST provides a lot of functionalities out of the box like Pagination and Event Handling.
Of course, just like Spring Data JPA, you can customize it to the core using annotations mentioned here.
Almost At The Victory Road:
That’s it for part 4. The only way to learn Spring is by doing. So go ahead and build your own REST APIs using Spring Boot.
You can find part 3 here: Link
In the next part, we will look at hardening our APIs using Spring Security.
“If you’ve come this far, what choice do you have but to keep going?”




