CRUD with Spring Boot
This post gives a high-level overview of creating REST APIs with Spring Boot. The objective is to create a minimalistic REST API say "/posts"
API that performs CRUD operations with HTTP verbs POST
, PUT
, GET
, and DELETE
.
Define/Design API:
Assume the context path of the app is "/blog"
. That said, the API would be "/blog/posts"
.
The first step is to define the supported operations and endpoints behavior.
-
Create new post
POST /blog/posts content-type: application/json { "title": "srk", "content" : "a-z" }
- Creates a new blog post.
- Return HTTP status code
- On successful creation of new post -
201
-Created
- If the post already exists in the system -
409
-Conflict
- For exception or error in the system. -
500
-Internal Server Error
- On successful creation of new post -
-
Sample
201
Response:{ "id": "1af1828f-9c2b-3312-984d-eb0c2d20d876", "title": "srk", "content": "a-z", "lastModified": "2023-08-25T05:36:14.460532956Z" }
-
Retrieve all posts
GET /blog/posts
-
Fetches all the posts in the system and returns an array of posts. Assumption is that the number of posts in the system is very small say <= 10. Let’s not talk about what if there are 100k posts just to keep things simple for now.
- Return HTTP status codes
- On successful fetch of all posts from the system -
200
-OK
- For exception or error in the system. -
500
-Internal Server Error
- On successful fetch of all posts from the system -
-
Sample
200
response[ { "id": "1af1828f-9c2b-3312-984d-eb0c2d20d876", "title": "srk", "content": "a-z", "lastModified": "2023-08-25T05:34:37.493472824Z" } ]
-
-
Get a specific post by id
GET /blog/posts/{id}
- Retrieves a specific post from the system matching
id
- Return HTTP status codes
- If the system cannot understand the request -
400
-Bad Request
- If the resource to be retrieved is not found in the system -
404
-Not Found
- On successful fetch of specific post from the system -
200
-OK
- If the system cannot understand the request -
-
Sample
200
response{ "id": "1af1828f-9c2b-3312-984d-eb0c2d20d876", "title": "srk", "content": "a-z", "lastModified": "2023-08-25T05:36:14.460532956Z" }
- Retrieves a specific post from the system matching
-
Update a specific post
PUT /blog/posts/{id} Content-Type: application/json { "title": "srk-update", "content": "a-z-update" }
- Return HTTP status codes
- If the system cannot understand the request -
400
-Bad Request
- If the resource to be updated is not found in the system -
404
-Not Found
- On successful update -
200
-OK
- If the system cannot understand the request -
-
Sample
200
Response:{ "id": "1af1828f-9c2b-3312-984d-eb0c2d20d876", "title": "srk-update", "content": "a-z-update", "lastModified": "2023-08-25T06:01:50.222741937Z" }
- Return HTTP status codes
-
Delete a specific post
DELETE /blog/post/{id}
- Return HTTP status codes
- If the system cannot understand the request -
400
-Bad Request
- If the resource to be deleted is not found in the system -
404
-Not Found
- On successful update -
204
-No Content
- If the system cannot understand the request -
- Return HTTP status codes
Implementation:
The vital part of implementation is Controller
which acts as an interface between api consumers and other layers like Service, DAO within the system. So, controller layer is emphasized in this post.
Here is the controller code
package crud;
import java.net.URI;
import java.util.Set;
import java.util.UUID;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author sairaghavak
*/
@RestController
public class BlogController {
@Autowired private BlogService blogService;
private static final String INVALID_UUID = "Invalid UUID";
@PostMapping("/posts")
public ResponseEntity<Post> createPost(@RequestBody Post post) {
if (!blogService.getPost(post.getId()).isEmpty()) {
return ResponseEntity.status(HttpStatus.CONFLICT.value()).build();
} else if (blogService.createPost(post)) {
return ResponseEntity.created(URI.create("/blog/posts/" + post.getId()))
.body(post);
} else {
return ResponseEntity.internalServerError().build();
}
}
@GetMapping("/posts")
public ResponseEntity<Set<Post>> getPosts() {
Set<Post> posts = blogService.getPosts();
return ResponseEntity.ok(posts);
}
@GetMapping("/posts/{id}")
public ResponseEntity<?> getPost(@PathVariable("id") String id) {
if (!blogService.isValidUUID(id)) {
return ResponseEntity.badRequest().body(INVALID_UUID);
}
Post post = blogService.getPost(UUID.fromString(id)).orElse(null);
if (post == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(post);
}
@PutMapping("/posts/{id}")
public ResponseEntity<?> updatePost(@PathVariable("id") String id,
@RequestBody Post post) {
if (!blogService.isValidUUID(id)) {
return ResponseEntity.badRequest().body(INVALID_UUID);
}
Post updatedPost = blogService.updatePost(UUID.fromString(id), post);
if (updatedPost == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(updatedPost);
}
@DeleteMapping("/posts/{id}")
public ResponseEntity<?> deletePost(@PathVariable("id") String id) {
if (!blogService.isValidUUID(id)) {
return ResponseEntity.badRequest().body(INVALID_UUID);
}
boolean isPostDeleted = blogService.deletePost(UUID.fromString(id));
if (!isPostDeleted) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
}
For complete implementation refer my GitHub repo spring-boot-crud-sample
References: