This is part 2 of the multi part posts, where you will learn how to create a blog engine with Reactjs & Spring Boot backend. In part 1 of this post, you followed a tutorial and saw code samples for the reactjs based front-end. In summary, you saw the process of how to create a react app, add components to CRUD blogpost and how to navigate between them using react router. Following on from part 1 in this post, you will go through the tutorial of how to create a backend for your front-end using Spring Boot. In this solution to save data, you will be using an in-memory database called the H2 database. All the source code comes from the Github repo, a link to which is included later in this post. Before you get started with building out the backend, it would help to refresh your memory and have a background of the last post.
Blog Engine
The end goal of this project is to build a simple, yet fully functional blog engine. It will be a full stack solution whereby you will build both the front end as well as the backend. In terms of the technologies, they are as follows,
Frontend | Backend |
ReactJS: reactjs frontend framework with Javascript React-router: for navigation React-redux: for state | Spring Boot: for blog API JWT: for security H2: relational database engine Lombok: to reduce boilerplate code |
How to create spring boot backend
As mentioned before, the backend you will create will be using Spring Boot framework. The Spring boot framework is a powerful framework that comes with production ready features and auto-configuration that facilitates rapid application development. Before you see the code for your backend, it would help to visualise how your code is structured.
Class diagram (ASCII)
+--------------------+
| BlogController |
+--------------------+
| - blogService: BlogService |
+--------------------+
| + addNewpost(post: Blogpost): BlogPost |
| + getPost(): BlogPost |
| + getAllPosts(): List<BlogPost> |
| + getPostsByUsername(id: String): Void |
| + updatePost(id: Long, body: BlogPost): Void |
| + deletePost(id: Long): Void |
+--------------------+
|
|
|
*
+--------------------+
| BlogService |
+--------------------+
| - blogRepository: BlogRepository |
+--------------------+
| + createNewPost(blogPost: BlogPost): BlogPost |
| + getBlogPostBy(id: String): List<BlogPost>
| + findAll(): List<BlogPost> |
| + getPostsByUser(username: String): List<BlogPost> |
| + updatePost(id: Long, body: BlogPost): BlogPost |
| + deletePost(id: Long): Void |
+--------------------+
|
|
|
*
+--------------------+
| BlogRepository extends JpaRespository<String, BlogPost> |
+--------------------+
| + findAll(): List<BlogPost> |
| + save(book: Book): BlogPost |
| + deleteById(id: Long): void|
| + findById(id: Long): Optional<BlogPost> |
| + findPostsByUser(id: Long): List<BlogPost> |
+--------------------+
|
|
|
*
+--------------------+
| Book |
+--------------------+
| - id: Long |
| - title: String |
| - content: String |
| - datePublished: String |
| - username: String |
+--------------------+
| + getters(): Object |
| + setters(): Void |
+--------------------+
Implementation code
Now, that you understand the class hierarchy of this solution, let’s see some code samples.
BlogController
@RestController
@RequestMapping("/api/blog")
public class BlogController {
private final BlogService blogService;
public BlogController(BlogService blogService) {
this.blogService = blogService;
}
@PostMapping("/post")
@ResponseStatus(HttpStatus.CREATED)
public BlogPost addNewPost(@RequestBody @Valid BlogPost post) {
return blogService.createNewPost(post);
}
@GetMapping("/addMockPosts")
public BlogPost createDummyPost() {
BlogPost post = BlogPost.builder()
.title("New Blog")
.content("Lots of new content to keep coming and coming soon")
.username("bhuman@mydaytodo.com")
.build();
return blogService.createNewPost(post);
}
@GetMapping("/post/{id}")
@ResponseStatus(HttpStatus.OK)
public BlogPost getBlogPost(@PathVariable("id") String id) {
return blogService.getBlogPostBy(id); // to be completed later
}
@GetMapping("/post/")
@ResponseStatus(HttpStatus.OK)
public List<BlogPost> getAllPosts() {
return blogService.findAll(); // to be completed later
}
@PatchMapping("/post/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public BlogPost updatePost(@PathVariable("id") String id, @RequestBody @Valid BlogPost post) {
return blogService.updatePost(id, post);
}
@DeleteMapping("/post/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deletePost(@PathVariable("id") String id) {
blogService.deletePost(id);
}
@GetMapping("/post/by/{username}")
@ResponseStatus(HttpStatus.OK)
public List<BlogPost> getPostsByUsername(@PathVariable("username") String username) {
return blogService.getPostsByUser(username);
}
}
BlogService
@Service
@Slf4j
public class BlogService {
@Autowired
private BlogRepository blogRepository;
public BlogService(BlogRepository blogRepository) {
this.blogRepository = blogRepository;
}
public BlogPost createNewPost(BlogPost blogPost) {
blogPost.setDatePublished(new Date());
return blogRepository.save(blogPost);
}
public BlogPost getBlogPostBy(String id) {
return blogRepository.findById(id).orElseThrow();
}
public List<BlogPost> findAll(){
return blogRepository.findAll();
}
public List<BlogPost> getPostsByUser(String username) {
return blogRepository.findPostsByUser(username);
}
public void deletePost(String id) {
blogRepository.deleteById(id);
}
public BlogPost updatePost(String id, BlogPost body) {
BlogPost post = blogRepository.findById(id).orElseThrow();
if(body.getContent() != null) {
post.setContent(body.getContent());
}
if(body.getTitle() != null) {
post.setTitle(body.getTitle());
}
return blogRepository.save(post);
}
}
BlogRepository
@Repository
public interface BlogRepository extends JpaRepository<BlogPost, String> {
@Query(value = "select * from blogpost where username = :username", nativeQuery = true)
List<BlogPost> findPostsByUser(@Param("username") String username);
}
SpringBoot main class
@SpringBootApplication
@ComponentScan({"com.mydaytodo.blog_backend.repository", "com.mydaytodo.blog_backend.controller", "com.mydaytodo.blog_backend.service"})
public class BlogBackendApplication {
public static void main(String[] args) {
SpringApplication.run(BlogBackendApplication.class, args);
}
}
H2 database
In terms of the H2 database it will be an in-memory database that will be created every time you start the spring boot application. Hence you need scripts to create the database tables when you start the database. To do so, create a file called data.sql and save it in the resources directory,
CREATE TABLE IF NOT EXISTS blogpost (id VARCHAR(9) PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255), content VARCHAR(255)
, date_published DATE, username VARCHAR(255));
Also, update your application.properties file as follows,
spring.application.name=blog-backend
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
Before wrapping up this post, let’s see the pom.xml file for this project.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mydaytodo</groupId>
<artifactId>blog-backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>blog-backend</name>
<description>Backend for blog engine</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-r2dbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
How to run the backend
To run this app, simply execute
mvn spring-boot:run
on the command line.
Summary
In the part 2 of this post you learned how to build a Spring Boot powered backend for your full stack blog engine application. You can see entire source code for this project here on Github. In the next post, you will see how to apply CI/CD pipelines and run this full stack app using Docker and Github Actions.
If you find any of my posts useful and want to support me, you can buy me a coffee 🙂
https://www.buymeacoffee.com/bhumansoni
While you are here, maybe try one of my apps for the iPhone.
Products – My Day To-Do (mydaytodo.com)
Here are some of my other bloposts,
How to unit test react-redux app – My Day To-Do (mydaytodo.com)
How to build a full stack Spring boot API with ReactJS frontend – My Day To-Do (mydaytodo.com)
How to call REST API with WebClient – My Day To-Do (mydaytodo.com)
How to build a jokes client in Java Spring Boot with RestTemplate – My Day To-Do (mydaytodo.com)
Have a read of some of my other posts on AWS
Upload to AWS S3 bucket from Java Spring Boot app – My Day To-Do (mydaytodo.com)
Deploy NodeJS, Typescript app on AWS Elastic beanstalk – (mydaytodo.com)
How to deploy spring boot app to AWS & serve via https – My Day To-Do (mydaytodo.com)
1 Comment
How to build a blog engine with React & Spring Boot – Part 3 CI/CD pipeline · October 4, 2024 at 1:27 pm
[…] How to build a blog engine with React & Spring Boot – Part 2 – My Day To-Do (mydaytodo.com) […]