This is the second post about a quick serie in which I intend to demonstrate some features in the use of Spring Boot framework, as well as the basic concepts regarding microservices.
In this post, I’m gonna show you how to create a RESTful controller to expose an API for CRUD operations. You can find the complete code here.
Spring Boot serie:
- Setting up the first project
- Implementing a RESTful API
What is RESTful?
First of all, let’s talk a bit about the REST concept. According to the Oracle documentation:
Representational State Transfer (REST) is an architectural style that specifies constraints […]. In the REST architectural style, data and functionality are considered resources and are accessed using Uniform Resource Identifiers (URIs), typically links on the Web. The resources are acted upon by using a set of simple, well-defined operations. The REST architectural style constrains an architecture to a client/server architecture and is designed to use a stateless communication protocol, typically HTTP.
REST is the most common and simplest way to expose HTTP services in webapps nowadays. RESTful is how we can call an application that makes use of REST power, using HTTP verbs to manage the resources – GET to obtain, POST to create, PUT to update, DELETE to remove and so on.
Ok, enough talk! Let’s improve the demo-service application created in the first part of this serie.
The domain object
Firstly, let’s create a domain class (within a package named domain) with four attributes: id, name, createdTimestamp and updatedTimestamp.
// package declaration and imports
@Data
public class User {
private Long id;
private String name;
private Calendar createdTimestamp;
private Calendar updatedTimestamp;
}
User class is just a simple POJO. Although very easy, I’m not gonna show how to create an entity class (at least not in this post) because this is just a very simple and quick example.
Notice that I’m using the @Data annotation at the class level. This annotation belongs to the Project Lombok, a Java library that makes easy and less repetitive to write code in Java.
If you don’t want to use this library, you just have to complete the code with the getters and setters for all attributes. If you do want to use, however, add the dependency to the pom.xml:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
The repository class
Like I said before, I’m not gonna show in this post how to configure a “real” repository to connect to a database neither to an external dependency. Because this is just an example, let’s create a simple repository class which will perform the CRUD operations in a java.util.Map implementation.
The class is called UserRepository and can be put in a repository package. This is the code:
// package declaration and imports
@Repository
public class UserRepository {
private static final Map<Long, User> REPOSITORY = new TreeMap<>();
private final AtomicLong sequence = new AtomicLong();
public User create(@NonNull final User user) {
Objects.requireNonNull(user.getName());
user.setId(sequence.incrementAndGet());
user.setCreatedTimestamp(Calendar.getInstance());
user.setUpdatedTimestamp(null);
REPOSITORY.put(user.getId(), user);
return user;
}
public void delete(@NonNull final Long id) {
REPOSITORY.remove(id);
}
public User get(@NonNull final Long id) {
return REPOSITORY.get(id);
}
public Collection<User> getAll() {
return REPOSITORY.values();
}
public User update(@NonNull final User user) {
final User currentUser = get(user.getId());
Objects.requireNonNull(currentUser);
currentUser.setName(user.getName());
currentUser.setUpdatedTimestamp(Calendar.getInstance());
REPOSITORY.put(currentUser.getId(), currentUser);
return currentUser;
}
}
Notice the @Repository annotation. This annotation indicates to Spring that this class is a mechanism for storage, retrieval and search operations against a collection of objects. The annotation also tells Spring that this class is eligible for autowiring.
So far, so good. We already have a repository with full support for CRUD operations. Let’s move on to the controller.
The RESTful controller
The first thing to do is to create the controller with the basic configuration, mapping the URL to the /users path. We can then seize the opportunity to create the first method for getting all the users in the repository.
// package declaration and imports
@RequestMapping("/users")
@RestController
public class UserController {
@Autowired
private UserRepository repository;
@GetMapping
public List<User> readAll() {
return new ArrayList<>(repository.getAll());
}
}
We can now run the main class of the project and then access the main URL via browser or command line:
$ curl -i http://localhost:8080/users HTTP/1.1 200 []
None result, because we don’t have any user yet, of course. So, what about to add a method to create users?
@PostMapping
public ResponseEntity<User> create(@RequestBody final User user) {
final User createdUser = repository.create(user);
final URI createdUserURI = URI.create("/users/" + createdUser.getId());
return ResponseEntity.created(createdUserURI).body(createdUser);
}
There are some few important things to notice here:
- @RequestBody is the annotation to indicate an HTTP body parameter, like our domain model User;
- ResponseEntity is the class to use representing an HTTP response provided with headers, body and status code.
In the example above, we create the response entity with HTTP status code CREATED (201), setting the URI where we can find the user afterwards. Let’s try:
$ curl -X POST -i http://localhost:8080/users -H 'content-type: application/json' -d '{ "name": "User name" }'
HTTP/1.1 201
Location: /users/1
{"id":1,"name":"User name","createdTimestamp":1506040921235,"updatedTimestamp":null}
Nice, right? Notice the Location header in the response and try to access it.
Wait… we don’t have a method to get the user by its ID. So, let’s create it:
@GetMapping("/{id}")
public ResponseEntity<User> read(@PathVariable("id") final Long id) {
final User user = repository.get(id);
return user == null
? ResponseEntity.notFound().build()
: ResponseEntity.ok(user);
}
One different thing in the code above is the id argument with @PathVariable annotation, which tells Spring to inject the value obtained in the URL from {id} placeholder.
It’s also important to note how we create the response entity. If we have a user with the informed id in our repository, we return his data with an HTTP status code OK (200). Otherwise, the response will be NOT FOUND (404), respecting the W3C status code definitions.
Now we can try to access the URL received in the create user method response:
$ curl -i http://localhost:8080/users/1
HTTP/1.1 200
{"id":1,"name":"User name","createdTimestamp":1506040921235,"updatedTimestamp":null}
We already have methods to read and insert users. Let’s implement an update method:
@PutMapping
public ResponseEntity<User> update(@RequestBody final User user) {
final User updatedUser = repository.update(user);
return ResponseEntity.ok(updatedUser);
}
Nothing special here.
$ curl -X PUT -i http://localhost:8080/users -H 'content-type: application/json' -d '{ "id": 1, "name": "Updated user" }'
HTTP/1.1 200
{"id":1,"name":"Updated user","createdTimestamp":1506040921235,"updatedTimestamp":1506302132944}
Last but not least, the delete method implementation:
@DeleteMapping("/{id}")
public ResponseEntity<?> delete(@PathVariable("id") final Long id) {
repository.delete(id);
return ResponseEntity.noContent().build();
}
The response will contains no content, which is the HTTP status code 204. Try it:
$ curl -X DELETE -i http://localhost:8080/users/1 HTTP/1.1 204
That’s all for now. I hope this post is helpful for you, and if you have any questions, please use the comments below. 🙂
See you soon.

