Building an API with Spring Boot

Building an API with Spring Boot

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:

  1. Simplified deployments, you get one jar which you can go ahead and deploy (just note however, this is not how it was always done, there is the whole war + tomcat pattern as well).
  2. Auto Configuration, at first it seems like magic, you put something in application.properties and it gets picked up, you add an annotation like @RestController and it automatically gets registered. Sometimes this magic can get scary as well, at least for the uninitiated, for example if you have a h2 dependency in your classpath, Spring assumes you are repositories are all using H2 database.
  3. Easier to manage dependencies. You can just go to Spring Initializr, pick the set of things that you want to use and then get going.

There are three main concepts you have to be aware of while building a CRUD application using Spring Boot ecosystem:

  1. Entities. These are domain entities, or classes that represent the core of your domain. For example in a food delivery application it would be the restaurant, food item, driver, user etc..
  2. Repositories. These are a layer of abstraction above the database that allow you to interact with it. In the Spring world a common way is to extend the JpaRepository, more on this later.
  3. Controllers. You have the repositories, sometimes if you have complex logic you also have a Service layer take care of that logic and then delegate the reads and writes to the repository later, now you need to expose APIs on top of those, for this you will use the Controller. In the Spring world, you basically need to annotate the Controller class with an @RestController if you wan to build a REST application.

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:

  1. Spring Boot for the application.
  2. H2 for the database, this is a pretty cool in-memory database that is JPA compliant that also supports disk based persistence if we need that, this is also entirely written in Java.
  3. HURL for URL testing.

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:

  1. Spring Web
  2. H2 Database
  3. Spring Data JPA
  4. Lombok

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:

  1. Define the entity
  2. Create the repository
  3. Ask copilot to generate the controller

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:

  1. @Entity defines an entity that gets persisted in the database, it generally needs a @Id and a @GeneratedValue on how the ID will be generated for that entity.
  2. @RestController and @RequestMapping mainly used to define the controller and give it a path, along with this you can have mapping such as @GetMapping or @PostMapping which tells what type of API call will happen on the method, see the controller above.
  3. @Value, @ConfigurationProperties for all things configuration related, you can read more about them here.

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.

Adil Fulara

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.

要查看或添加评论,请登录

Prathik Rajendran M的更多文章

  • Bringing Your A-Game

    Bringing Your A-Game

    A-Game is basically when a person is operating at the peak of their abilities. I have come to the conclusion that it's…

    2 条评论
  • Building Things, Part 1: Finding that one person to build for

    Building Things, Part 1: Finding that one person to build for

    Building Things is a series of articles on what it takes to build a useful product written from an engineer's…

    1 条评论
  • Inner Game of Software Engineering

    Inner Game of Software Engineering

    Recently I finished reading a book called Inner Game of Tennis by Timothy Gallwey and it got me thinking about how that…

  • How to ensure your customers DON'T buy your product :)

    How to ensure your customers DON'T buy your product :)

    Back in 2018, I set out to buy a car. I was clear about what I wanted - an SUV, Automatic Transmission, fuel-efficiency…

社区洞察

其他会员也浏览了