avatarIvan Franchin

Summary

The provided content is a comprehensive guide detailing the backend implementation for a "StarVote" application using Spring Boot, with the option to integrate Keycloak or Okta as Identity Providers (IdP).

Abstract

The article delves into the development of the StarVote application's backend, focusing on the model, services, and controller layers. It outlines the creation of the Star class to represent entities in the database, the StarRepository for database operations, and the DiceBearClient for avatar generation. The StarService interface and its implementation StarServiceImpl are introduced to handle business logic, including sorting stars by creation or votes and incrementing votes. The AddStarRequest class and StarAPIController are described to manage API requests for adding and retrieving stars. The article concludes with instructions for testing the API endpoints and setting up the application using an H2 in-memory database, as well as a teaser for the upcoming frontend implementation in the series.

Opinions

  • The author expresses enthusiasm about the StarVote application, indicating that it will be feature-rich and well-designed by the end of the tutorial series.
  • The use of DiceBear for avatar generation is recommended as a versatile and easy-to-use solution for creating avatars.
  • The article promotes the use of Spring Boot for its ease in setting up RESTful services and integrating with various components such as repositories and clients.
  • The author encourages reader engagement and support by inviting claps, shares, follows on social media, and subscriptions to a newsletter, indicating a desire for community building and feedback.
  • There is an implied preference for using Keycloak or Okta as IdP options, suggesting they are reliable choices for authentication and authorization in a Spring Boot application.

Building a Single Spring Boot App with Keycloak or Okta as IdP: Backend Implementation

A step-by-step guide for implementing the backend of the StarVote application

Photo by Dmitry Berdnyk on Unsplash

This article is part of a series that explores the implementation of a Single Spring Boot application called StarVote. The application will use Keycloak or Okta as Identity Provider.

In the introductory article, we outline the sections we will cover.

Here’s a sneak peek of how the StarVote application will be at the end!

In this particular article, we’ll focus on developing the backend of the StarVote application. We’ll explore topics such as the application model, services, and controller.

So, let’s get started!

Implementing Backend

Create the Star class

In the model package, create the Star class with the following content:

package com.example.singlestarvoteapp.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

import java.time.Instant;

@Data
@Entity
public class Star {

    @Id
    @GeneratedValue
    private Long id;

    @NotBlank
    private String name;

    @NotBlank
    private String avatarUrl;

    private int votes = 0;

    private Instant created = Instant.now();
}

The Star class is marked as an entity using the @Entity annotation, indicating that it will be stored in a database table. It uses the @Data annotation from Lombok to automatically generate common methods like getters, setters, equals(), hashCode(), and toString().

The class contains fields for the star's identifier, name, avatar URL, vote count, and creation timestamp. Required fields, like name and avatarUrl, must not be left blank. The votes field starts with a default value of 0, indicating the number of votes received. The created field captures the timestamp of the star's creation using the Instant.now() method.

Create the StarRepository class

Within the repository package, create the StarRepository class with the following content:

package com.example.singlestarvoteapp.repository;

import com.example.singlestarvoteapp.model.Star;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface StarRepository extends JpaRepository<Star, Long> {

    List<Star> findAllByOrderByCreatedDesc();

    List<Star> findAllByOrderByVotesDesc();
}

The StarRepository extends the JpaRepository interface, which provides ready-to-use methods for working with the database, allowing us to perform various operations, such as adding new stars and getting information about existing stars.

The StarRepository interface has two specific methods:

  • findAllByOrderByCreatedDesc(): this method retrieves all stars from the database and sorts them in descending order based on when they were created;
  • findAllByOrderByVotesDesc(): this method retrieves all stars and sorts them in descending order based on the number of votes they have received.

Create the DiceBearClient class

In the client package, create the DiceBearClient class with the following content:

package com.example.singlestarvoteapp.client;

import org.springframework.stereotype.Component;

@Component
public class DiceBearClient {

    public String getAvatar(String str) {
        return "https://api.dicebear.com/6.x/avataaars/svg?seed=%s".formatted(str);
    }
}

The DiceBearClient class is a Spring component that acts as a client for the DiceBear API.

DiceBear is a powerful platform for creating avatars quickly and easily. It offers diverse avatar styles, including abstract shapes and detailed characters, with options for both random and deterministic generation.

The DiceBearClient class provides a single method, getAvatar(), which takes a string parameter and returns the URL of an avatar image generated by the DiceBear API using the provided string as the seed.

Create the StarService interface

In service package, create the StarService interface with the following content:

package com.example.singlestarvoteapp.service;

import com.example.singlestarvoteapp.model.Star;

import java.util.List;

public interface StarService {

    List<Star> getStarsSortedByCreated();

    List<Star> getStarsSortedByVotes();

    Star saveStar(Star star);

    Star getStarById(Long id);

    void incrementStarVotes(Star star);
}

The StarService interface defines the contract for operations related to stars in the StarVote application.

Create the StarServiceImpl class

Additionally, in the service package, create the StarServiceImpl class that implements the StarService interface, and include the following content:

package com.example.singlestarvoteapp.service;

import com.example.singlestarvoteapp.client.DiceBearClient;
import com.example.singlestarvoteapp.model.Star;
import com.example.singlestarvoteapp.repository.StarRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@RequiredArgsConstructor
@Service
public class StarServiceImpl implements StarService {

    private final StarRepository starRepository;
    private final DiceBearClient diceBearClient;

    @Override
    public List<Star> getStarsSortedByCreated() {
        return starRepository.findAllByOrderByCreatedDesc();
    }

    @Override
    public List<Star> getStarsSortedByVotes() {
        return starRepository.findAllByOrderByVotesDesc();
    }

    @Override
    public Star saveStar(Star star) {
        star.setAvatarUrl(diceBearClient.getAvatar(star.getName()));
        return starRepository.save(star);
    }

    @Override
    public Star getStarById(Long id) {
        return starRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Star with id '%s' not found!".formatted(id)));
    }

    @Override
    public void incrementStarVotes(Star star) {
        star.setVotes(star.getVotes() + 1);
        saveStar(star);
    }
}

The @RequiredArgsConstructor is a Lombok annotation that automatically generates a constructor with required arguments based on the final fields in the class, starRepository and diceBearClient.

The @Service indicates that this class is a service component and should be automatically detected and managed by the Spring framework.

The implementation of the interface methods in StarServiceImpl utilizes the injected dependencies, starRepository and diceBearClient:

  • getStarsSortedByCreated() and getStarsSortedByVotes() methods delegate the calls to the starRepository instance to retrieve the list of stars in the desired order.
  • saveStar(Star star) sets the avatar URL of the star by calling the getAvatar() method of the diceBearClient instance and then saves the star using the starRepository.
  • getStarById(Long id) retrieves a star from the starRepository instance based on the provided ID.
  • incrementStarVotes(Star star) increments the vote count of the star by one and saves the updated star using the saveStar() method.

Create the AddStarRequest class

In controller package, create the AddStarRequest class with the following content:

package com.example.singlestarvoteapp.controller;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class AddStarRequest {

    @NotBlank
    private String name;
}

The AddStarRequest class is a simple request object used in the application's controller layer. It contains a single field name, which represents the name of the star to be added. The @NotBlank annotation ensures that the name field must have a non-blank value when making a request. The class is enhanced with Lombok's @Data.

Create the ActorAPIController class

Within the controller package, create the ActorAPIController class and include the following content:

package com.example.singlestarvoteapp.controller;

import com.example.singlestarvoteapp.model.Star;
import com.example.singlestarvoteapp.service.StarService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
public class StarAPIController {

    private final StarService starService;

    @GetMapping("/stars")
    public List<Star> getStars() {
        return starService.getStarsSortedByCreated();
    }

    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping("/stars")
    public Star addStar(@Valid @RequestBody AddStarRequest addStarRequest) {
        Star star = new Star();
        star.setName(addStarRequest.getName());
        star = starService.saveStar(star);
        return star;
    }
}

The StarAPIController class is a RESTful API controller that handles HTTP requests. Here are the key points:

  • It is annotated with @RestController, indicating that it's a specialized controller for RESTful APIs.
  • The @RequestMapping annotation specifies the base URL mapping for the API endpoints handled by this controller.
  • The getStars() method handles GET requests to retrieve a list of stars sorted by creation timestamp.
  • The addStar() method handles POST requests to add new stars.

Update the application.properties

Let’s open the application.properties file and add the following lines:

spring.application.name=star-vote

spring.datasource.url=jdbc:h2:mem:appdb
spring.datasource.username=sa
spring.datasource.password=sa
  • The spring.application.name property sets the name of the Spring application to “star-vote”.
  • The spring.datasource properties define the configuration for the H2 in-memory database. The URL is set to connect to the "appdb" database, while the username and password for the database connection are both set to "sa".

Testing StarVote API

To start the StarVote application, open a terminal and navigate to the root folder of the application. Then, run the following command:

./mvnw clean spring-boot:run

Once the application is started, we can submit requests to its endpoints. Let’s begin by retrieving all the stars. Open another terminal and run the following command:

curl -i http://localhost:8080/api/stars

This command should return an HTTP 200 response with an empty array [], indicating that no stars have been added yet.

Let’s add a star. For it, use the following curl command to submit a POST request:

curl -i -X POST http://localhost:8080/api/stars \
  -H 'Content-Type: application/json' -d '{"name": "Margot Robbie"}'

This command should return an HTTP 201 response with the details of the newly added star, including its unique ID, name, avatar URL, vote count, and creation timestamp.

To verify that the star has been successfully added, retrieve all stars again by running the following command:

curl -i http://localhost:8080/api/stars

This time, the command should return an HTTP 200 response with an array containing the previously added star’s details.

Congratulations! The StarVote API is up and running, and we have successfully tested its functionality. 🎉

To shut it down, go to the terminal where it’s running and press Ctrl+C.

Up Next

We will implement the frontend of the StarVote application.

We hope you are enjoying the StarVote Tutorial series!

Support and Engagement

If you enjoyed this article and would like to show your support, please consider taking the following actions:

  • 👏 Engage by clapping, highlighting, and replying to my story. I’ll be happy to answer any of your questions;
  • 🌐 Share my story on Social Media;
  • 🔔 Follow me on: Medium | LinkedIn | Twitter;
  • ✉️ Subscribe to my newsletter, so you don’t miss out on my latest posts.
Spring Boot
Keycloak
Okta
Technology
Software Development
Recommended from ReadMedium