Building an API with Spring Boot
Prathik Rajendran M
Sr Staff Engineer @ Coupang | Distributed Systems | Software Architecture
I have never been exposed to the Spring Ecosystem, till I joined Coupang , we are pretty big on Spring Framework, Spring Boot in our stack. My last five years have been mostly Dropwizard and Go (in Tala & GoTo Group ) and Clojure (in GoTo Group ), and I was not a big fan of the Spring way of doing things either, I'd say even now I am not a convert but my views have changed a bit after some weekend hacking.
This article is for people who are pretty new to Spring ecosystem just like me, I will give an overview of things I learnt while building a simple CRUD application (which to be honest, more applications are) and how Copilot helped me build it really quickly.
Spring Boot philosophy centers around three core things:
There are three main concepts you have to be aware of while building a CRUD application using Spring Boot ecosystem:
My goal was to build a simple TODO application, which is pretty much a cliche these days, any new technology that you want to try out, you do this with a TODO application. My TODO had a bit of a variant, you would first capture all the TODOs into an Inbox, once you have captured in the Inbox, you can process them into concrete Actions or if an Inbox item has more than 1 action, you make a Project out of it. This is basically a trimmed down version of the Getting Things Done framework, something I use on a regular basis with the Todoist application.
To build this I used the following stack:
The first step is to go to Spring Initializr, select Gradle for project management, Java as your language, Spring Boot version of 3.3.4, fill in the project meta data. You also need to add the following dependencies:
You can generate and then open the downloaded content in an IDE, preferably IntelliJ. Its a big help if you have copilot on the side.
Building the application is as simple as the following:
领英推荐
Check the below Inbox entity InboxItem.java and the repository for it, the rest has literally no work from my side.
// InboxItem.java
package com.coderprathik.gtd.inbox;
import jakarta.persistence.*;
import lombok.Data;
@Entity
@Data
public class InboxItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
}
// InboxItemRepository.java
package com.coderprathik.gtd.inbox;
import org.springframework.data.jpa.repository.JpaRepository;
public interface InboxItemRepository extends JpaRepository<InboxItem, Long> {
}
Once I had the entity up, all I needed to do is ask Github Copilot to generate the Controller for me and it did that with no errors at all, exactly what I wanted from a CRUD api for the entity.
Below is the generated code from CoPilot
package com.coderprathik.gtd.inbox;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/inbox-items")
public class InboxItemController {
private final InboxItemRepository inboxItemRepository;
public InboxItemController(InboxItemRepository inboxItemRepository) {
this.inboxItemRepository = inboxItemRepository;
}
// Create a new InboxItem
@PostMapping
public InboxItem createInboxItem(@RequestBody InboxItem inboxItem) {
return inboxItemRepository.save(inboxItem);
}
// Get all InboxItems
@GetMapping
public List<InboxItem> getAllInboxItems() {
return inboxItemRepository.findAll();
}
// Get a single InboxItem by ID
@GetMapping("/{id}")
public ResponseEntity<InboxItem> getInboxItemById(@PathVariable Long id) {
Optional<InboxItem> inboxItem = inboxItemRepository.findById(id);
return inboxItem.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
}
// Update an InboxItem
@PutMapping("/{id}")
public ResponseEntity<InboxItem> updateInboxItem(@PathVariable Long id, @RequestBody InboxItem inboxItemDetails) {
Optional<InboxItem> optionalInboxItem = inboxItemRepository.findById(id);
if (optionalInboxItem.isPresent()) {
InboxItem inboxItem = optionalInboxItem.get();
inboxItem.setTitle(inboxItemDetails.getTitle());
return ResponseEntity.ok(inboxItemRepository.save(inboxItem));
} else {
return ResponseEntity.notFound().build();
}
}
// Delete an InboxItem
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteInboxItem(@PathVariable Long id) {
if (inboxItemRepository.existsById(id)) {
inboxItemRepository.deleteById(id);
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
}
}
Couple of key annotations to keep in mind:
Spring is a very big ecosystem, there are mechanisms to do cron jobs and batch jobs in you application, you can use Spring Cloud Stream to interact with systems like Kafka and rabbitmq, there are repositories for other databases such as MySQL, Postgres etc.. and there is even support for document dbs like MongoDB and Graph DB such as Neo4J.
I liked using Spring for simple applications like this, Spring generally goes with the convention over configuration approach, one of the things that worry me about convention over configuration is that it comes at a risk of flexibility. Whenever your application deviates from a standard pattern, it takes a lot of effort to change the convention provided.
The other thing about using framework like Spring is that it has a learning curve. If you are in Go, for example, if you know the basic language constructs you have enough tools to build your micro-service which is highly flexible, yes it trades off with time, but with things like CoPilot the effort is really low.
Overall, if I am building a simple CRUD application for a POC, I would go with Spring. It took me under 10 minutes to build the entire application with multiple endpoints and hurl tests with spring and copilot, that time saved in the start could mean that I spend more time building for the customer.
Would I build a more complex application with this? I am not sure, I am always worried that using a framework will limit my flexibility, but I still need to dive deep more on Spring Framework to understand how we can achieve complex workflows in those. For now, I'll stick to my preferred Go in case I have to recommend a language / tool to build a complex application.
Staff Software Engineer at Intuit
5 个月Great introductory article. When done right, SB delivers significant value for the Java shops. Once a developer understands the IoC concepts and the dependency modeling, then spring way just works.