Fullstack app: Developing the Back-End with Spring Boot and PostgreSQL

mspilari

Matheus Bernardes Spilari

Posted on September 30, 2024

Fullstack app: Developing the Back-End with Spring Boot and PostgreSQL

In this article we are going to create the backend that will be consumed by our Vite/React frontend.

Create the project

At the Spring Initializr create a project with the following dependencies:

  • Spring Data JPA
  • Spring Web
  • PostgreSQL Driver

I'm using Java 21 with Maven, Jar and Spring Boot version 3.3.3.

The group name is com.spring, the artifact and name is backend.

Generate the project and extract the directory.

Move the backend directory to the fullstackapp directory you created when you build the frontend.


Code

1.Configuring application.properties

In your application.properties put the code below:

spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/database}
spring.datasource.username=user
spring.datasource.password=1234

spring.jpa.hibernate.ddl-auto=update

cors.allowed-origins=${CORS_ALLOWED_ORIGINS:http://localhost:5173}
Enter fullscreen mode Exit fullscreen mode

2. Configs directory

Inside the directories java > com > spring > backend create the configs directory, inside of that create a file called WebConfig.java.

This file will be responsible for configuring CORS:

WebConfog.java

package com.spring.render.configs;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig {

    private String allowedOrigins;

    public WebConfig(@Value("${cors.allowed-origins}") String allowedOrigins) {
        this.allowedOrigins = allowedOrigins;
    }

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins(allowedOrigins.split(","))
                        .allowedMethods("GET", "POST", "PUT", "DELETE")
                        .allowedHeaders("*");
            }
        };

    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation

  • cors.allowed-origins: This is an environment variable that will be passed through Docker.

3. Models directory

Inside the directories java > com > spring > backend create the models directory, inside of that create a file called MessageModel.java.

This will be our model, with the respective getters, setters, and constructors.

MessageModel.java

package com.spring.render.models;

import java.util.UUID;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Table(name = "tb_messages")
@Entity
public class MessageModel {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    private String message;

    public MessageModel() {
    }

    public MessageModel(String message) {
        this.message = message;
    }

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

Enter fullscreen mode Exit fullscreen mode

4. Repositories directory

Inside the directories java > com > spring > backend create the repositories directory, inside of that create a file called MessageRepository.java.

This file comes with some built-in methods for working with your database, such as save, delete, findAll, and findById. You can also write additional queries or methods inside the interface.

MessageRepository.java

package com.spring.render.repositories;

import java.util.UUID;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.spring.render.models.MessageModel;

@Repository
public interface MessageRepository extends JpaRepository<MessageModel, UUID> {

}
Enter fullscreen mode Exit fullscreen mode

5. Services directory

Inside the directories java > com > spring > backend create the services directory, inside of that create a file called MessageService.java.

This is where the business logic of your app resides. We have three methods:

  • allMessages: Retrieves all messages from the database.
  • createMessage: Saves a new message to the database.
  • deleteMessage: Deletes a message from the database based on its ID.
package com.spring.render.services;

import java.util.List;
import java.util.UUID;

import org.springframework.stereotype.Service;

import com.spring.render.models.MessageModel;
import com.spring.render.repositories.MessageRepository;

@Service
public class MessageService {

    private MessageRepository messageRepository;

    public MessageService(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }

    public List<MessageModel> allMessages() {
        return messageRepository.findAll();
    }

    public MessageModel createMessage(String message) {

        var newMessage = new MessageModel(message);

        return messageRepository.save(newMessage);
    }

    public void deleteMessage(String messageId) {

        messageRepository.deleteById(UUID.fromString(messageId));
    }

}

Enter fullscreen mode Exit fullscreen mode

6. Controllers directory

Inside the directories java > com > spring > backend create the controllers directory, inside of that create a file called MessageController.java.

Here we have three endpoints to our api:

  • /allmessages: Retrieve all messages from the database.
  • /sendmessage: To send a new message to the database.
  • /deletemessage/{id}: Delete a message passing his ID as a parameter.

MessageController.java

package com.spring.render.controllers;

import java.util.List;

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.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.spring.render.dto.MessageDto;
import com.spring.render.models.MessageModel;
import com.spring.render.services.MessageService;

@RestController
public class MessageController {

    private MessageService messageService;

    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }

    @GetMapping("/allmessages")
    public ResponseEntity<List<MessageModel>> getAllMessages() {
        var messageList = messageService.allMessages();

        return ResponseEntity.ok().body(messageList);
    }

    @PostMapping("/sendmessage")
    public ResponseEntity<MessageModel> postMessage(@RequestBody MessageDto messageDto) {
        var newMessage = messageService.createMessage(messageDto.message());

        return ResponseEntity.status(HttpStatus.CREATED).body(newMessage);
    }

    @DeleteMapping("/deletemessage/{id}")
    public ResponseEntity<String> deleteMessage(@PathVariable("id") String id) {

        messageService.deleteMessage(id);

        return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null);
    }
}
Enter fullscreen mode Exit fullscreen mode

7. Create the Dockerfile

Inside the backend directory, create the Dockerfile, which will assist with deployment.

If you are using another version of Java, change the image version from eclipse-temurin.

I'm using the Alpine version of the image because it's lightweight. Install additional packages if needed.

Dockerfile

FROM eclipse-temurin:21-jdk-alpine
COPY . .
RUN ./mvnw clean install -DskipTests
ENTRYPOINT ["java","-jar","target/render-0.0.1-SNAPSHOT.jar"]
Enter fullscreen mode Exit fullscreen mode

Conclusion

With that, our backend is ready to be consumed by our frontend and is prepared for deployment.

We will cover the deployment in the next article.

Thanks for reading !!

📍 Reference

👋 Talk to me

💖 💪 🙅 🚩
mspilari
Matheus Bernardes Spilari

Posted on September 30, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related