Millie K. Mastering Full Stack Development With Spring Boot 3 and React...2024
Millie K. Mastering Full Stack Development With Spring Boot 3 and React...2024
By
Katie Millie
Copyright notice
Copyright © 2024 Katie Millie. All rights Reserved.
This body of work, which includes all written content, visual
materials, and programming code, is the sole intellectual
property of Katie Millie. Any form of unauthorized use,
reproduction, or distribution of this material is strictly
prohibited and will require prior written authorization. The
information contained within this work is provided
exclusively for informational purposes and should not be
interpreted as legal or professional advice.
Katie Millie explicitly reserves the right to pursue legal
action against any party that infringes upon or misuses this
content. For any permissions, requests, or inquiries, direct
communication with Katie Millie is required. It is of the
highest priority to protect the originality and integrity of this
work.
We kindly ask for your compliance with these terms to
ensure the continued respect and protection of intellectual
property rights. Thank you for your understanding and
cooperation.
Unleash the Power of Your
Full Stack Skills!
We hope Mastering Full Stack Development with Spring Boot
3 & React has equipped you to build dynamic web
applications. Your feedback is vital in shaping future
editions.
INTRODUCTION
Forge Your Path to Full-Stack Mastery: Spring Boot 3
& React
Code Examples:
Learning Resources:
Terminology:
JavaScript
function Welcome(props)
return
<h1>Hello, {props.name}!</h1>
Java
@RestController
public class UserController
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable
Long id)
// Fetches user data from database (logic not shown)
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
JavaScript
import axios from 'axios';
function UserList()
const [users, setUsers] = useState([]);
useEffect
axios.get('/api/users')
.then(response => setUsers(response.data));
return
<ul>
{users.map(user
<li key={user.id}>{user.name}</li>
)
</ul>
Java
@RestController
public class HelloController
@GetMapping("/hello")
public String helloWorld()
return "Hello from Spring Boot 3!";
JavaScript
import React, { useState, useEffect } from 'react';
function HelloComponent()
const [message, setMessage] = useState('');
useEffecte
fetch('/hello')
.then(response => response.text())
.then(data => setMessage(data));
},
return
<div>
<h1>{message}</h1>
</div>
}
export default HelloComponent;
1. Back-End Technologies:
Java
@SpringBootApplication
public class SpringBootApp
public static void main(String[] args)
SpringApplication.run(SpringBootApp.class, args);
This code snippet demonstrates how Spring Boot
automatically configures a Spring application, eliminating
the need for extensive configuration files.
● Reactive Programming Support: Enables
building responsive and scalable applications that
can handle high volumes of concurrent requests.
● Improved Security: Provides enhanced security
features like Spring Security for robust web services.
● Java: As the primary language for Spring Boot 3,
Java offers a robust and mature object-oriented
programming paradigm for building back-end
functionalities.
● Relational Databases (MySQL, PostgreSQL):
These store and manage persistent data accessed
by the application. Spring Data JPA simplifies
interacting with these databases using an object-
relational mapping (ORM) approach.
2. Front-End Technologies:
JavaScript
import React from 'react';
function HelloComponent
return
<div>
<h1>Hello from React!</h1>
</div>
);
export default HelloComponent;
This component displays a simple heading "Hello from
React!". React's key advantages include:
● Component-Based Architecture: Simplifies UI
development and promotes code reusability.
● Virtual DOM: Enables efficient UI updates, leading
to smoother rendering performance.
● JSX Syntax: Combines HTML-like structure with
JavaScript for a more intuitive development
experience.
● HTML, CSS, and JavaScript: These fundamental
web development technologies form the core
building blocks of any user interface.
● JavaScript Frameworks (Optional): While React
can handle complex functionalities, additional
frameworks like Redux can be used for intricate
application state management.
3. Additional Technologies:
Java
@RestController
public class MessageController
@GetMapping("/message")
public String getMessage
return "Hello from Spring Boot 3!";
Front-End (React):
JavaScript
import React, { useState, useEffect } from 'react';
function MessageComponent
const [message, setMessage] = useState('');
useEffect
fetch('/message')
.then(response => response.text())
.then(data => setMessage(data));
},
return
<div>
<h1>{message}</h1>
</div>
);
export default MessageComponent;
Java
@RestController
public class UserController
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody
User user)
// Implement logic to save user to database
return new ResponseEntity<>(user,
HttpStatus.CREATED);
project-name/
├── backend/ # Spring Boot 3 Back-End
│ ├── src/
│ │ └── main/
│ │ ├── java/ # Java Source Code
│ │ │ └── com/yourcompany/yourapp/ # Your
Application Package
│ │ │ ├── # Your Java Code Files
│ │ └── resources/ # Resources (e.g.,
application.properties)
│ └── pom.xml # Maven Project Object Model
├── frontend/ # React Front-End
│ ├── public/ # Static Assets (e.g., images, fonts)
│ ├── src/ # React Source Code
│ │ ├── App.js # Main Application Entry Point
│ │ ├── components/ # Reusable UI Components
│ │ ├── pages/ # Individual Application Pages
│ │ ├── services/ # API Interaction Logic
│ │ └── # Other React Code Files
│ └── package.json # Project Dependencies and Scripts
└── # Other Project Files (e.g., README.md)
Java
// Model (User.java)
public class User
private Long id;
private String name;
private String email;
// Getters and Setters
// Controller (UserController.java)
@RestController
@RequestMapping("/api/users")
public class UserController
@Autowired
private UserService userService;
@GetMapping
public List<User> getAllUsers()
return userService.getAllUsers();
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody
User user)
User newUser = userService.createUser(user);
return new ResponseEntity<>(newUser,
HttpStatus.CREATED);
}
// Service (UserService.java)
public interface UserService
List<User> getAllUsers();
User createUser(User user);
JavaScript
// App.js
import React, { useState, useEffect } from 'react';
import UsersList from './components/UsersList';
function App()
const [users, setUsers] = useState;
useEffect
fetch('/api/users')
.then(response => response.json())
.then(data => setUsers(data));
},
return
<div className="App">
<h1>User List</h1>
<UsersList users={users} />
</div>
);
export default App;
// UsersList.js (Component)
import React from 'react';
function UsersList({ users })
return
<ul>
{users.map(user
<li key={user.id}>{user.name}</li>
)
</ul>
);
export default UsersList;
Installation Steps
Windows:
macOS:
Linux:
2. Verifying Installation:
Java
java -version
Windows:
Bash
export JAVA_HOME=/usr/local/java/jdk-17.0.3
export PATH=$PATH:$JAVA_HOME/bin
Java
javac -version
Bash
node -v
npm -v
Bash
npx create-react-app my-spring-boot-app
Bash
cd my-spring-boot-app
Bash
npm start
Next Steps:
With React set up, you can start developing the user
interface components for your Spring Boot application.
We've laid the foundation for full-stack development using
Spring Boot 3 and React. However, mastering these
technologies requires further exploration. Here are some
resources to help you on your journey:
● Spring Web
● Spring Data JPA (if using a database)
● WebMvc (for RESTful APIs)
● Lombok (optional, but simplifies boilerplate code)
Java
@SpringBootApplication
public class Application
public static void main(String[] args)
SpringApplication.run(Application.class, args);
The @SpringBootApplication annotation tells Spring Boot to
scan for components and configurations within this package
and its sub-packages. Spring Application is then launched
using the run method.
1. Launch VS Code.
3. Create a new folder for your React project. You can do this
within VS Code itself using the built-in file explorer.
4. Initialize the React Project (using Create React
App):
Bash
npx create-react-app my-react-app
Bash
cd my-react-app
Bash
npm start
Now that you have both your Spring Boot backend and
React frontend set up in separate environments, you need
to establish communication between them. Here's a
simplified explanation:
● Spring Boot REST API: Create RESTful API
endpoints within your Spring Boot application to
expose data and functionalities. These endpoints
will be accessible via URLs.
● React API Calls: Use libraries like Axios within
your React components to make HTTP requests
(usually GET or POST) to your Spring Boot API
endpoints.
● Data Exchange: The Spring Boot API receives
requests, interacts with your data layer (e.g.,
database) if needed,and returns responses (usually
in JSON format) containing the requested data or
results.
● React Data Handling: React components receive
the JSON response from the Spring Boot API and
update the user interface accordingly, displaying the
retrieved data or handling the outcome of an
operation.
Next Steps:
Installation Steps
1. Downloading Node.js:
Windows:
macOS:
Linux:
2. Verifying Installation:
Bash
node -v
npm -v
Windows:
Bash
npm list -g --depth=0
Bash
npx create-react-app my-spring-boot-app
Bash
cd my-spring-boot-app
Bash
npm start
Next Steps:
With Node.js, npm, and a basic React project set up, you're
well on your way to developing the user interface
components for your Spring Boot application. Here are some
resources to guide you further:
Code Editors:
IDEs:
1. Installing VS Code:
Download and install VS Code from the official website:
https://code.visualstudio.com/download.
2. Installing Extensions:
Additional Tips:
1. Prerequisites:
If you prefer using the command line, you can use the
Spring Boot CLI (Command Line Interface). Install it
following the instructions on the Spring Boot website:
https://docs.spring.io/spring-boot/cli/index.html. Then, run
the following command:
Bash
spring init spring-boot-react-app --dependencies=web
Java
@SpringBootApplication
public class Application
public static void main(String[] args)
SpringApplication.run(Application.class, args);
Bash
mvn clean install
Bash
java -jar target/spring-boot-react-app.jar
Example (Simplified):
Java
@RestController
public class ProductController
private final ProductService productService; //
Dependency injected by Spring Boot 3
@Autowired
public ProductController(ProductService productService)
this.productService = productService;
}
@GetMapping("/products")
public List<Product> getAllProducts()
return productService.findAllProducts();
Example (Simplified):
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Benefits:
While you can work with the downloaded ZIP file directly,
consider importing it into an IDE (Integrated Development
Environment) like IntelliJ IDEA. This provides features like
code completion, debugging tools, and project management
for a more efficient development experience.
1. Navigate to src/main/java.
2. Open the class that extends
SpringBootApplication (usually named
Application.java). This class serves as the entry
point for your Spring Boot application.
Java
@SpringBootApplication
public class Application
public static void main(String[] args)
SpringApplication.run(Application.class, args);
The @SpringBootApplication annotation is crucial. It tells
Spring Boot 3 to scan for components and configurations
within this package and its sub-packages, automatically
configuring them based on conventions. This single
annotation simplifies configuration and kickstarts your
Spring Boot application.
Using Maven:
Bash
mvn clean install
Bash
./gradlew build
Bash
java -jar target/spring-boot-react-app.jar
Prerequisites:
If you prefer using the command line, install the Spring Boot
CLI (Command Line Interface) following the instructions on
the Spring Boot website: https://docs.spring.io/spring-
boot/cli/index.html. Then, run the following command:
Bash
spring init spring-boot-react-app --dependencies=web
Bash
Bash
./gradlew build
Example (Simplified):
JavaScript
function ProductCard(props)
return
<div className="product-card">
<img src={props.imageUrl} alt={props.productName}
<h3>{props.productName}</h3>
<p>{props.price}</p>
<button>Add to Cart</button>
</div>
Example (Simplified):
JavaScript
function App()
return (
<div className="App">
<h1>Hello, World!</h1>
</div>
Benefits of React:
● Improved Performance: React's virtual DOM and
efficient rendering mechanisms enhance
responsiveness and performance of your web
applications.
● Reusable Components: Component-based
architecture promotes code reusability, reducing
development time and code complexity.
● Declarative Programming: React focuses on
describing the desired UI state rather than explicitly
manipulating the DOM, leading to more intuitive and
maintainable code.
● Large Community and Ecosystem: React boasts
a vast and active community that constantly
contributes to its development and provides a wide
array of libraries and tools.
Bash
Example (Simplified):
Communication Mechanisms:
Component Types:
JavaScript
function ProductCard(props)
return
<div className="product-card">
<img src={props.imageUrl} alt={props.productName}
<h3>{props.productName}</h3>
<p>{props.price}</p>
<button>Add to Cart</button>
</div>
This ProductCard component displays product information
and a button. Notice how it accepts props (short for
properties) as arguments, allowing you to customize its
behavior and appearance.
Benefits of Components:
Example (JSX):
JavaScript
function App()
return
<div className="App">
<h1>Hello, World!</h1>
</div>
This code defines a simple React application with an <h1>
element displaying "Hello, World!".
JavaScript
function ProductList()
const products
{ id: 1, name: "Product 1", price: 10 },
{ id: 2, name: "Product 2", price: 20 },
];
return
<div className="product-list">
{products.map((product)
<ProductCard key={product.id} {product} /> //
Spread operator to pass all product properties
)
</div>
);
function ProductCard(props)
// Access props using destructuring or object notation
const { imageUrl, productName, price } = props;
// rest of the component logic
In this example, the ProductList component iterates through
an array of products and renders a ProductCard component
for each product. The ProductCard component received
props containing product details and utilizes them to display
the information.
Benefits of Props:
Updating State:
JavaScript
function Counter()
const [count, setCount] = useState(0);
const handleClick
setCount(count + 1);
};
return
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
In this example, the useState hook initializes a state
variable count with a value of 0. The handleClick function
increments the count and utilizes the setCount function
returned by useState to update the state.
JavaScript
Benefits of State:
Bash
Bash
cd my-first-react-app
JavaScript
JavaScript
JavaScript
Bash
Bash
cd my-spring-boot-react-app
Bash
JavaScript
import React from 'react';
function App()
return
<div className="App">
<h1>Hello, World!</h1>
</div>
);
export default App;
Bash
npm run build (or yarn build)
JavaScript
const element = document.getElementById('my-element');
function updateElement()
element.textContent = 'New Content';
element.style.color = 'red';
}
updateElement();
Java
public class UserService
private UserRepository userRepository;
public UserService()
this.userRepository = new UserRepository(); // Tight
coupling
}
public User getUserById(Long id)
return userRepository.findById(id);
Java
public class UserService
private final UserRepository userRepository; //
Dependency injection
public UserService(UserRepository userRepository)
this.userRepository = userRepository;
}
public User getUserById(Long id)
return userRepository.findById(id);
By injecting the UserRepository through the constructor, we
achieve several benefits:
Java
public interface UserRepository
User findById(Long id);
// Add other methods as needed
Java
@Repository
public class JpaUserRepository implements UserRepository
@Autowired
private EntityManager entityManager;
@Override
public User findById(Long id)
return entityManager.find(User.class, id);
3. Annotate for Injection: In your UserService,
use the @Autowired annotation to indicate that
Spring should inject the UserRepository
dependency:
Java
@Service
public class UserService
@Autowired
private final UserRepository userRepository;
// (rest of the class)
JavaScript
const UserContext = React.createContext(null);
JavaScript
function UserDetails()
const user = useContext(UserContext);
return
<div>
<h1>User Details</h1>
<p>Name: {user.name}</p> </div> );
In this example, the `UserDetails` component retrieves user
data from the context without needing to be explicitly
passed down props through the component hierarchy. This
promotes better code organization and avoids prop drilling,
a common issue in large React applications.
While the Context API isn't a true DI framework, it allows for
a form of state management that promotes loose coupling
between React components. This aligns with the principles
of DI by reducing dependencies between components and
facilitating maintainability.
Benefits of DI in Spring Boot 3 and React
The advantages of DI extend beyond the points mentioned
earlier. Here's a breakdown of some key benefits for both
server-side and client-side development:
● Improved Testability: As components rely on
injected dependencies, unit testing becomes easier.
Mock objects can be readily injected during tests,
isolating the logic under test.
● Enhanced Maintainability: Loose coupling allows
for easier modifications and updates to individual
components without cascading effects.
● Code Reusability: By focusing on interfaces
rather than concrete implementations, code
becomes more reusable across different parts of
your application.
● Flexibility: DI facilitates easier adoption of new
technologies or frameworks by allowing you to swap
out implementations without major code changes.
Dependency Injection plays a crucial role in building well-
structured, testable, and maintainable applications. Both
Spring Boot 3 and React, though on different sides of the
development spectrum, embrace principles of loose
coupling to create robust and adaptable software. By
understanding and leveraging DI in your projects, you can
streamline development, enhance code quality, and ensure
your applications are built for the future.
Java
public class UserService
private UserRepository userRepository = new
UserRepository(); // Tight coupling
public User getUserById(Long id)
return userRepository.findById(id);
This approach creates tight coupling. The UserService is
now dependent on a specific implementation of
UserRepository. Any changes to the repository would
necessitate modifications in the service class. Testing also
becomes cumbersome as mocking the repository becomes
necessary.
Java
public class UserService
private final UserRepository userRepository; //
Dependency injection
@Autowired
public UserService(UserRepository userRepository)
this.userRepository = userRepository;
}
public User getUserById(Long id)
return userRepository.findById(id);
By injecting the UserRepository through the constructor, we
achieve several benefits:
Java
public interface UserRepository
User findById(Long id);
// Add other methods as needed
Java
@Repository
public class JpaUserRepository implements UserRepository
@Autowired
private EntityManager entityManager;
@Override
public User findById(Long id) {
return entityManager.find(User.class, id);
Java
@Service
public class UserService
@Autowired
private final UserRepository userRepository;
// (rest of the class)
Spring Boot will automatically create an instance of
JpaUserRepository during application startup and inject it
into the UserService constructor.
1. Enhanced Testability:
Unit testing becomes a breeze with DI. You can readily inject
mock objects during tests, isolating the specific functionality
under test. Imagine needing to test the UserService logic
without involving the actual database interaction. By
injecting a mock UserRepository that returns predefined
user data, you can focus solely on the service's business
logic.
2. Improved Maintainability:
5. Improved Configurability:
Java
public class UserService
private UserRepository userRepository = new
UserRepository(); // Tight coupling
public User getUserById(Long id)
return userRepository.findById(id);
Java
public class UserService
private final UserRepository userRepository; //
Dependency injection
@Autowired
public UserService(UserRepository userRepository)
this.userRepository = userRepository;
// (rest of the class)
Java
public interface UserRepository
User findById(Long id);
// Add other methods as needed
Java
@Repository
public class JpaUserRepository implements UserRepository
@Autowired
private EntityManager entityManager;
@Override
public User findById(Long id)
return entityManager.find(User.class, id);
Java
@Service
public class UserService
@Autowired
private final UserRepository userRepository;
public User getUserById(Long id)
return userRepository.findById(id);
Java
public class OrderService
@Autowired
public OrderService(OrderRepository orderRepository,
PaymentService paymentService)
// (use injected dependencies)
● Setter Injection: Dependencies are injected
through setter methods. While less preferred than
constructor injection, it can be useful in specific
scenarios:
Java
public class ProductService
private ProductRepository productRepository;
@Autowired
public void setProductRepository(ProductRepository
productRepository)
this.productRepository = productRepository;
}
public Product getProductById(Long id)
return productRepository.findById(id);
Java
public class NotificationService
@Autowired
private EmailSender emailSender;
public void sendNotification(String message)
emailSender.sendEmail(message);
Java
return userRepository.findById(id);
this.userRepository = userRepository;
return userRepository.findById(id);
Java
Java
@Repository
@Autowired
@Override
Java
@Service
this.userRepository = userRepository;
return userRepository.findById(id);
Java
@Service
this.userRepository = userRepository;
Java
@Configuration
Java
@Configuration
@Bean
Java
Java
@Repository
@Autowired
@Override
Java
@Configuration
@Bean
@Bean
public UserService userService(UserRepository
userRepository)
What is JPA?
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Define Entities:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
EntityManager em =
entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist(newUser);
em.getTransaction().commit();
em.close();
Java
Java
user.setName("Updated Name");
em.merge(user);
Java
em.remove(user);
This code snippet retrieves all users with the name "John
Doe" using JPQL.
Frontend (React):
JavaScript
function UserList()
useEffect
fetch('/api/users')
return
<div>
<h2>Users</h2>
<ul>
{users.map(user
<li key={user.id}>{user.name}</li>
</ul>
<div>
This component:
Setting Up a Database
Connection (e.g., MySQL,
PostgreSQL)
Setting Up a Database Connection in Spring Boot 3 for Full-
Stack Development with React
Prerequisites
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Using application.properties
Properties
spring.datasource.url=jdbc:mysql://localhost:3306/your_dat
abase_name # Replace with your details
spring.datasource.username=your_username
spring.datasource.password=your_password
Bash
export
SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/you
r_database_name
export SPRING_DATASOURCE_USERNAME=your_username
export SPRING_DATASOURCE_PASSWORD=your_password
Verifying Connection
XML
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
XML
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
Understanding Entities
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Explanation:
This entity class defines the user data model. JPA will map it
to a table named "users" (by default) in the database. You'll
need to create getters and setters for each field (not shown
here) for proper data access.
Java
@Entity
Java
@Entity
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
@Entity
@GeneratedValue(strategy = GenerationType.IDENTITY)
In this example:
JPA Repositories
Java
Java
Java
@Service
@Autowired
return userRepository.findAll();
return userRepository.findById(id).orElse(null);
}
// Implement methods for creating, updating, and
deleting users
Java
@RestController
@RequestMapping("/api/users")
@Autowired
@GetMapping
return ResponseEntity.ok(userService.getAllUsers());
}
@GetMapping("/{id}")
return ResponseEntity.ok(userService.getUserById(id));
JavaScript
useEffect
fetch('/api/users')
},
return
<div>
<h2>Users</h2>
<ul>
{users.map(user
<li key={user.id}>{user.name}</li> }
</ul>
</div>
This component:
Project Setup
● Spring Web
● Spring Data JPA
Set Up Database Connection: Configure your database
connection details (URL, username, password) in the
application.properties file as discussed in the previous
article (Assuming MySQL or PostgreSQL).
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
Java
@Service
@Autowired
return taskRepository.findAll();
return taskRepository.findById(id).orElse(null);
Task existingTask =
taskRepository.findById(id).orElse(null);
if (existingTask == null)
existingTask.setTitle(updatedTask.getTitle());
existingTask.setDescription(updatedTask.getDescriptio
n());
existingTask.setCompleted(updatedTask.getCompleted
());
return taskRepository.save(existingTask);
taskRepository.deleteById(id);
Java
@RestController
@RequestMapping("/api/tasks")
@Autowired
@GetMapping
return ResponseEntity.ok(taskService.getAllTasks());
@GetMapping("/{id}")
return ResponseEntity.ok(taskService.getTaskById(id));
@PostMapping
public ResponseEntity<Task> createTask(@RequestBody
Task newTask)
return
ResponseEntity.ok(taskService.createTask(newTask));
@PutMapping("/{id}")
return ResponseEntity.ok(taskService.updateTask(id,
updatedTask));
JavaScript
function TaskList()
useEffect
fetch('/api/tasks')
},
JavaScript
function TaskForm()
event.preventDefault();
fetch('/api/tasks',
method: 'POST',
.then(newTask
});
setTitle('');
setDescription('');
};
1. Project Setup
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
3. User Repository
Java
4. User Controller
Java
@RestController
@RequestMapping("/api/users")
@Autowired
@GetMapping
return userRepository.findAll();
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id)
@PostMapping
return userRepository.save(user);
@PutMapping("/{id}")
User existingUser =
userRepository.findById(id).orElseThrow(() -> new
ResourceNotFoundException("User not found with id: " +
id));
existingUser.setName(updatedUser.getName());
existingUser.setEmail(updatedUser.getEmail());
return userRepository.save(existingUser);
@DeleteMapping("/{id}")
userRepository.deleteById(id);
return ResponseEntity.noContent().build();
1. Project Setup
2. Dependencies
JavaScript
const UserList
useEffect
setUsers(response.data);
fetchUsers();
});
return
<div>
<h2>Users</h2>
<ul>
{users.map((user)
</ul>
</div>
This component:
4. App.js
JavaScript
const App
return
<div className="App">
<UserList />
</div>
Additional Considerations
1. Project Setup:
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
3. User Repository
Java
4. User Controller
Java
@RestController
@RequestMapping("/api/v1/users")
@Autowired
@GetMapping
public List<User> getAllUsers()
return userRepository.findAll();
@GetMapping("/{id}")
if (user.isPresent
return ResponseEntity.ok(user.get
else
return ResponseEntity.notFound().build();
@PostMapping
return
ResponseEntity.created(URI.create("/api/v1/users/" +
savedUser.getId())).body(savedUser);
@PutMapping("/{id}")
if (existingUser.isPresent;
updatedUser.setId(id);
User savedUser =
userRepository.save(updatedUser);
return ResponseEntity.ok(savedUser);
else
return ResponseEntity.notFound().build();
@DeleteMapping("/{id}")
userRepository.deleteById(id);
return ResponseEntity.noContent().build();
1. Project Setup:
2. Dependencies:
JavaScript
useEffect
setUsers(response.data);
};
fetchUsers();
},
return
<div>
<h2>Users</h2>
<ul>
{users.map((user)
</ul>
</div>
);
export default UserList;
This component:
4. App.js
JavaScript
const App
return
<div className="App">
<UserList />
</div>
);
Additional Considerations
Security Considerations
Prerequisites:
● Basic understanding of Spring Boot and React
● Familiarity with HTTP methods (GET, POST, PUT,
DELETE)
Building a Controller:
Java
@Controller
Java
@GetMapping("/products")
return productService.findAllProducts();
Java
@GetMapping("/products/{id}")
return productService.findProductById(id);
Java
@PostMapping("/products")
return productService.saveProduct(newProduct);
Handling Responses:
Error Handling:
JavaScript
JavaScript
method: 'POST',
body: JSON.stringify(newProduct)
Prerequisites:
Java
@RestController
@Autowired
Java
@GetMapping("/products")
return productService.findAllProducts();
Java
@GetMapping("/products/{id}")
return productService.findProductById(id);
Java
@PostMapping("/products")
Error Handling:
JavaScript
JavaScript
method: 'POST',
body: JSON.stringify(newProduct)
});
if (!response.ok)
}
const createdProduct = await response.json();
Additional Considerations:
Prerequisites:
Java
@Controller
@Autowired
Java
@GetMapping("/products")
return productService.findAllProducts();
Java
@GetMapping("/products/{id}")
return productService.findProductById(id);
Java
@PostMapping("/products")
return productService.saveProduct(newProduct);
Java
@PutMapping("/products/{id}")
return productService.updateProduct(updatedProduct);
Java
@DeleteMapping("/products/{id}")
productService.deleteProduct(id);
return ResponseEntity.noContent().build();
● @DeleteMapping("/products/{id}"): Maps to
DELETE requests at "/api/v1/products/10".
● @PathVariable Long id: Extracts the ID from the
URL.
● productService.deleteProduct(id): Calls the service
method to delete the product with the provided ID.
● return ResponseEntity.noContent().build(): Returns
an empty response with a 204 No Content status
code, indicating successful deletion.
JavaScript
const fetchProducts = async
if (!response.ok)
JavaScript
method: 'POST',
body: JSON.stringify(newProduct)
if (!response.ok)
JavaScript
method: 'PUT',
body: JSON.stringify(updatedProduct)
});
if (!response.ok)
throw new Error('Failed to update product');
JavaScript
method: 'DELETE'
});
if (!response.ok)
Additional Considerations:
● Security: Implement security measures like
authentication and authorization (e.g., Spring
Security) to control access to your endpoints based
on user roles and permissions.
● Error Handling: Implement robust error handling
on both the server-side (Spring controllers) and
client-side (React application) to provide informative
error messages to users in case of request failures.
● Validation: Validate user input on both the server-
side (using JSR-303 annotations or a dedicated
validation framework) and client-side (using React
validation libraries) to ensure data integrity and
prevent invalid data from being submitted.
Prerequisites:
Project Setup:
● Spring Web
● Spring Data JPA
● H2 Database (in-memory for development)
● Lombok (optional, for boilerplate reduction)
Bash
cd crud-app
Backend Development:
● Entity Model (Java): Define a Java class
representing the data you want to manage:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
Java
@RestController
@RequestMapping("/api/tasks")
public class TaskController
@Autowired
this.taskRepository = taskRepository;
@GetMapping
return taskRepository.findAll();
@PostMapping
return taskRepository.save(task);
@SpringBootApplication
SpringApplication.run(CrudApplication.class, args);
@Configuration
@Bean
dataSource.setUrl("jdbc:h2:mem:crud"); // H2 in-
memory database
dataSource.setUsername("sa");
dataSource.setPassword;
return dataSource;
Frontend Development:
const TaskList
useEffect
setTasks(response.data);
};
fetchData();
},
await
axios.delete(`http://localhost:8080/api/tasks/${id}`); //
Delete task by ID
setTasks(remainingTasks);
};
return
<ul>
{tasks.map((task)
<li key={task.id}>
{task.title} - {task.description}
</li>
</ul>
);
JavaScript
const App
return
<Router>
<Routes>
</Routes>
</Router>
);
JavaScript
const TaskForm
e.preventDefault();
setDescription('');
};
return
<form onSubmit={handleSubmit}>
<label>
Title:
</label>
<label>
Description:
</label>
</form>
Remember:
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
Java
@Configuration
@EnableWebSecurity
// Configure Authentication
@Override
protected void configure(AuthenticationManagerBuilder
auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.roles("USER")
.and()
.withUser("admin")
.roles("ADMIN");
// Configure Authorization
@Override
http
.authorizeRequests()
.anyRequest().authenticated() // Require
authentication for all other requests
.and()
.and()
Explanation:
● configure(AuthenticationManagerBuilder) defines
how users will be authenticated. Here, we use in-
memory authentication for demonstration purposes.
In a real application, you'd likely use a database or
LDAP integration.
● configure(HttpSecurity) defines authorization rules
for different parts of your application. We allow
public access to specific paths (/public/**), restrict
admin URLs to users with the ADMIN role, and
require authentication for all other requests.
● We enable form login (formLogin()) with default
behavior, allowing users to submit a username and
password through a login form. Additionally, HTTP
Basic authentication (httpBasic()) is enabled for
more flexibility (optional).
Java
@Service
@Autowired
@Override
User user =
userRepository.findByUsername(username);
if (user == null)
Advanced Features
Implementing User
Authentication with Spring
Security in Spring Boot 3 and
React
Securing your Spring Boot application with user
authentication is essential for protecting sensitive data and
functionalities. Spring Security offers a comprehensive
framework to achieve robust authentication and
authorization mechanisms. This article guides you through
implementing user authentication with Spring Security in a
Spring Boot 3 application with a React frontend,
incorporating code examples for each step.
Prerequisites
● Basic understanding of Spring Boot and React
development.
● Familiarity with Spring Security concepts
(authentication, authorization).
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
@Service
@Autowired
@Override
User user =
userRepository.findByUsername(username);
if (user == null)
Java
@Component
@Override
return new
BCryptPasswordEncoder().encode(rawPassword.toString());
@Override
return new
BCryptPasswordEncoder().matches(rawPassword.toString(),
encodedPassword);
Java
@Configuration
@EnableWebSecurity
@Autowired
@Autowired
@Override
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder); // Use
custom encoder if implemented
@Override
http
.authorizeRequests()
.anyRequest().authenticated() // Require
authentication for other requests
.and()
.formLogin().loginPage("/login").permitAll() //
Login form at /login
.and()
.logout().logoutUrl("/logout").logoutSuccessHandl
er(new LogoutSuccessHandler() {
@Override
public void
onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication
authentication) throws IOException {
response.sendRedirect("/login"); // Redirect
to login after logout
permitAll();
Explanation:
JavaScript
const LoginComponent
e.preventDefault();
try
method: 'POST',
});
if (!response.ok)
throw new Error('Login failed');
setError(null);
catch (err)
setError(err.message);
return
<div>
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<label htmlFor="username">Username:</label>
<label htmlFor="password">Password:</label>
<button type="submit">Login</button>
{error && <p className="error">{error}</p>}
</form>
</div>
);
Explanation:
Java
@RestController
@RequestMapping("/api")
@Autowired
loginRequest.getUsername(),
loginRequest.getPassword());
Authentication authentication =
authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(
authentication);
Explanation:
Java
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ManyToMany(fetch = FetchType.EAGER)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
UserDetailsService Implementation:
Java
@Override
if (user == null)
this.user = user;
Security Configuration:
● Create a SecurityConfig class extending
WebSecurityConfigurerAdapter.
● Configure user details service, password encoder
(e.g., BCryptPasswordEncoder), and authorization
rules.
● Use antMatchers methods to define protected
endpoints and access restrictions using hasRole or
hasAnyRoleannotations.
Java
@Configuration
@EnableWebSecurity
@Autowired
@Override
.authorizeRequests()
.antMatchers("/api/public").permitAll() // Public
endpoint
.antMatchers("/api/admin").hasRole("ADMIN") // Only
admins can access
.anyRequest().authenticated()
.and()
.userDetailsService(userDetailsService)
@Override
auth.userDetailsService(userDetailsService).passwordEnc
oder(passwordEncoder());
@Bean
Frontend (React):
1. Project Setup:
2. Authorization Context:
JavaScript
import React, { createContext, useState, useEffect } from
'react';
currentUser: null,
isAuth: false,
roles:
setToken: ,
});
useEffect
if (storedToken)
.then(response
setCurrentUser(response.data.user);
setIsAuth(true);
setRoles(response.data.user.roles.map(role =>
role.name));
})
.catch
};
localStorage.setItem('authToken', token);
};
return
{children}
</AuthContext.Provider>
);
JavaScript
return (props)
return <div>Unauthorized</div>;
};
const AdminPage
};
5. Session Management:
7. Code Security:
8. Security Testing:
9. API Security:
Java
@Configuration
@EnableWebSecurity
@Autowired
private MyUserDetailsService userDetailsService;
@Override
.authorizeRequests()
.antMatchers("/api/public").permitAll()
.antMatchers("/api/users/").hasRole("ADMIN") // Only
admins can manage users
.anyRequest().authenticated()
.and()
.userDetailsService(userDetailsService)
@Override
auth.userDetailsService(userDetailsService).passwordEnc
oder(passwordEncoder()
}
@Bean
Practical Example:
Implementing User Login and
Role-Based Security with
Spring Boot 3 and React
Securing user access and controlling functionalities based
on roles is crucial for modern web applications. This guide
demonstrates implementing user login and role-based
access control (RBAC) using Spring Boot 3 for the backend
API and React for the frontend, drawing inspiration from
concepts in "Mastering Full Stack Development with Spring
Boot 3 and React" (without referring to specific pages).
Project Setup:
Entity Definitions:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ManyToMany(fetch = FetchType.EAGER)
@Entity
@GeneratedValue(strategy = GenerationType.IDENTITY)
UserDetailsService:
Java
@Autowired
@Override
if (user == null)
}
return new MyUserDetails(user);
this.user = user;
Security Configuration:
Java
@Configuration
@EnableWebSecurity
@Override
.authorizeRequests()
.antMatchers("/api/public").permitAll() // Public
endpoint
.antMatchers("/api/admin/").hasRole("ADMIN") // Only
admins can access admin endpoints
.antMatchers("/api/users/").hasAnyRole("ADMIN",
"EDITOR") // Admins and editors can access user endpoints
.anyRequest().authenticated()
.and()
.userDetailsService(userDetailsService)
@Override
@Bean
Frontend (React):
Project Setup:
Authorization Context:
JavaScript
currentUser: null,
isAuth: false,
roles:
setToken:
});
useEffect
if (storedToken)
.then(response
setCurrentUser(response.data.user);
setIsAuth(true);
setRoles(response.data.user.roles.map(role =>
role.name));
})
.catch
});
try {
localStorage.setItem('authToken', token);
} catch (error)
const logout
localStorage.removeItem('authToken');
setCurrentUser(null);
setIsAuth(false);
setRoles;
};
return
{children}
</AuthContext.Provider>
);
Protected Routes:
JavaScript
return (props)
return <div>Unauthorized</div>;
};
const AdminPage
};
JavaScript
return
<button type="button" className={`btn btn-${variant}
btn-${size}`} onClick={onClick}>
{label}
</button>
);
Button.propTypes
label: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
Additional Tips
JavaScript
function Counter()
const increment
};
return
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
In this example:
The JSX code displays the current count value and the
button that triggers the increment.
Java
@Service
@Autowired
return productRepository.findAll();
JavaScript
import React, { useState, useEffect } from 'react';
function ProductList()
useEffect
Explanation:
Additional Considerations:
JavaScript
function ButtonComponent()
const handleClick
setCount(count + 1);
};
return
In this example:
JavaScript
function ProductList()
setProducts(data);
useEffect
fetchProducts();
},
return
<div>
<button onClick={fetchProducts}>Load
Products</button>
<ul>
{products.map((product)
<li key={product.id}>{product.name}</li>
</ul>
</div>
Java
@RestController
@RequestMapping("/api")
Explanation:
1. React Code:
JavaScript
return
<div className="card">
<h3>{title}</h3>
<p>{content}</p>
</div>
JavaScript
JavaScript
const addToCart
return
<div className="product-card">
<h3>{product.name}</h3>
<p>{product.price}</p>
</div>
);
JavaScript
return
<div className="product-list">
{products.map((product)
</div>
);
JavaScript
{props.isLoading ? <p>Loading.</p> :
<WrappedComponent {props} />}
</div>
);
The Spring Boot 3 backend acts as the data source for the
React frontend. The product listing page in our example
would likely fetch product data from the backend using API
calls. Here's a high-level overview:
JavaScript
function TodoList()
useEffect
};
fetchTasks();
},
event.preventDefault();
if (!newTask) return;
const newTodo
text: newTask,
completed: false,
};
method: 'POST',
body: JSON.stringify(newTodo),
});
setTasks([...tasks, createdTask]);
setNewTask('');
};
const handleToggleCompletion = async (taskId)
updatedTask.completed = !updatedTask.completed;
method: 'PUT',
body: JSON.stringify(updatedTask),
});
setTasks
return
<div className="todo-list">
<h2>To-Do List</h2>
<form onSubmit={handleAddTask}>
<button type="submit">Add</button>
</form>
<ul>
{tasks.map((task)
{task.text}
</li>
</ul>
</div>
);
Explanation:
1. TodoController.java:
Java
@RestController
@RequestMapping("/api/todos")
@Autowired
@GetMapping
return todoService.findAllTodos();
@PostMapping
return ResponseEntity.ok(createdTodo);
@PutMapping("/{id}")
public ResponseEntity<Todo> updateTodo(@PathVariable
Long id, @RequestBody Todo updatedTodo)
if (!id.equals(updatedTodo.getId
return ResponseEntity.ok(savedTodo);
Explanation:
2. TodoService.java (interface):
Java
3. TodoServiceImpl.java (implementation):
Java
@Service
@Autowired
@Override
return todoRepository.findAll();
@Override
return todoRepository.save(todo);
@Override
return todoRepository.save(todo);
Explanation:
TypeScript
interface ProductProps
name: string;
price: number;
}
// component logic
TypeScript
interface Product
id: number;
name: string;
price: number;
@RestController
@GetMapping("/products")
return products;
Frontend (React):
TypeScript
interface ProductProps
products: Product[];
return
<ul>
{products.map((product)
<li key={product.id}>
{product.name} - ${product.price}
</li>
</ul>
Setting Up TypeScript in a
React Project
TypeScript, with its emphasis on static typing, seamlessly
integrates with React to empower you with a more robust
and maintainable development experience. This guide walks
you through incorporating TypeScript into your React
project,aligning it with a potential Spring Boot 3 backend.
We'll explore the setup process, explore code examples, and
discuss the benefits of this approach.
Prerequisites:
Bash
Bash
npm init -y
Bash
JSON
"compilerOptions":
JSON
"scripts":
TypeScript
interface ProductProps
name: string;
price: number;
return
<div>
<h2>{name}</h2>
<p>Price: ${price}</p>
</div>
);
TypeScript
interface Product
id: number;
name: string;
price: number;
@RestController
// interface Product
id: number;
name: string;
price: number;
@RestController
@GetMapping("/products")
];
return products;
Frontend (React):
TypeScript
interface ProductProps
products: Product[];
return
<ul>
{products.map((product)
<li key={product.id}>
{product.name} - ${product.price}
</li>
</ul>
);
Java
Example:
Java
@Controller
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
return ResponseEntity.ok(user);
Example:
TypeScript
interface User
id: number;
name: string;
id: 1,
name: "Alice",
};
TypeScript
interface UserListProps
users: User[];
}
// component logic
return
<ul>
{props.users.map((user)
<li key={user.id}>{user.name}</li>
</ul>
Java
@Controller
@Autowired
@GetMapping("/products/{id}")
public ResponseEntity<Product>
getProductById(@PathVariable Long id)
return ResponseEntity.ok(product);
Example:
TypeScript
type UserAddress
street: string;
city: string;
};
interface User
id: number;
name: string;
address: UserAddress;
Benefits of Interfaces
Java
public interface UserRepository extends
JpaRepository<User, Long>
Java
List<User> getAllUsers();
// other methods
@Service
Interfaces in React
JavaScript
interface UserProps
username: string;
email: string;
return
<div>
<h2>{props.username}</h2>
<p>{props.email}</p>
</div>
JavaScript
type UseAuthState
isLoggedIn: boolean;
Further Exploration:
Frontend (React):
● A UserList component displaying a list of users.
● A UserForm component for creating new users.
1. UserList Component:
TypeScript
// User.ts
interface User
id: number;
username: string;
email: string;
TypeScript
// UserList.tsx
interface UserListProps
users: User[];
<ul>
{users.map((user)
<li key={user.id}>
{user.username} - {user.email}
</li>
</ul>
2. UserForm Component:
TypeScript
// UserForm.ts
interface UserFormProps
e.preventDefault();
};
return
<form onSubmit={handleSubmit}>
</form>
The user data retrieved from the Spring Boot API should
ideally match the structure defined in the User interface. By
utilizing Spring Data JPA with entities and repositories, you
can ensure consistency.
For example, your Spring Boot User entity can map to the
User interface in your React component:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
return id;
this.id = id;
return username;
this.username = username;
return email;
this.email = email;
This approach ensures that the data received from the
Spring Boot API seamlessly fits the expectations of your
React components defined with the `User` interface.
Further Exploration:
JavaScript
function getTodos()
fetch('http://localhost:8080/api/todos')
.then(response => response.json()) // Parse JSON
response
.then(data
console.log('Todos:', data);
getTodos();
Explanation:
JavaScript
try
console.log('Todos:', data);
catch (error)
getTodos();
Explanation:
Benefits of Axios:
JavaScript
function TodoList()
useEffect
try
setTodos(response.data);
catch (error)
fetchData();
},
return
<div>
<h1>Todo List</h1>
<ul>
{todos.map(todo
<li key={todo.id}>{todo.title}</li>
</ul>
</div>
);
Explanation:
JavaScript
function TodoList
const [todos, setTodos] = useState;
useEffect
setIsLoading(true);
try
if (!response.ok)
setTodos(data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
},
// (render function with loading/error handling and todo list
display)
Explanation:
2. Using Axios:
JavaScript
function TodoList
setIsLoading(true);
try
setTodos(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
},
Explanation:
JavaScript
return
<div>
<h1>Todo List</h1>
{isLoading ?
<p>Loading Todos...</p>
: error ?
<p>Error: {error.message}</p>
<ul>
{todos.map(todo
<li key={todo.id}>{todo.title}</li>
</ul>
</div>
Explanation:
Additional Considerations
JavaScript
function UsersList()
useEffect
setIsLoading(true);
try
setUsers(data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
},
return
<div>
<h1>Users</h1>
{isLoading ?
<p>Loading Users...</p>
: error ?
<p>Error: {error.message}</p>
<ul>
{users.map(user
</ul>
</div>
);
Explanation:
2. Using Axios:
JavaScript
function UsersList()
useEffect
setIsLoading(true);
try
setUsers(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
// (render function with conditional rendering based on
state)
Explanation:
return
<div>
<h1>Users</h1>
{isLoading ? (
<p>Loading Users...</p>
: error ?
<p>Error: {error.message}</p>
):(
<ul>
{users.map(user
</ul>
</div>
Explanation:
JavaScript
function useFetch(url)
useEffect
setIsLoading(true);
try
if (!response.ok)
setData(data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
},
[url])
return { data, isLoading, error };
Explanation:
JavaScript
function UsersList()
const { data, isLoading, error } =
useFetch('http://localhost:8080/api/users');
return
<div>
<h1>Users</h1>
{isLoading ?
<p>Loading Users...</p>
: error ?
<p>Error: {error.message}</p>
):(
<ul>
{data.map(user
</ul>
</div>
);
Explanation:
JavaScript
};
Explanation:
JavaScript
setIsLoading(true);
try
url,
options,
});
setData(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
},
[url, options]);
1. Project Setup:
Java
package com.example.todoapp;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
this.title = title;
return id;
this.id = id;
return title;
this.title = title;
Java
package com.example.todoapp.controller;
import com.example.todoapp.model.Todo;
import com.example.todoapp.service.TodoService;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.web.bind.annotation.CrossOrigin;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@Autowired
@GetMapping("/api/todos")
return todoService.getAllTodos();
4. TodoService (Optional):
1. Project Setup:
JavaScript
useEffect
const fetchData = async
setIsLoading(true);
try
url,
options,
});
setData(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
[url, options]);
JavaScript
function TodoList()
return
<div>
<h1>Todos</h1>
{isLoading ?
<p>Loading Todos...</p>
: error ?
<p>Error: {error.message}</p>
):(
<ul>
{data.map(todo
<li key={todo.id}>{todo.title}</li>
</ul>
</div>
);
Explanation:
Bash
Bash
cd my-react-app
JavaScript
// App.js
function App()
return
<BrowserRouter>
<Routes>
</Routes>
</BrowserRouter>
);
JavaScript
// Header.js
function Header()
return
<header>
<Link to="/"Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</header>
);
function App()
return
<BrowserRouter>
<Header />
<Routes>
</Routes>
</BrowserRouter>
);
export default App;
Additional Considerations:
Bash
JavaScript
// App.js
function App()
return
<BrowserRouter>
<Routes>
</Routes>
</BrowserRouter>
);
JavaScript
// Header.js
import { Link } from 'react-router-dom';
function Header()
return
<header>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</header>
);
function App()
return
<BrowserRouter>
<Header />
<Routes>
</Routes>
</BrowserRouter>
);
Now you can extend this structure to define routes for more
complex pages in your application. Here are some
examples:
●
● In the ProductDetails component, you can access
the product ID from the useParams hook provided
by React Router:
● JavaScript
function ProductDetails()
const { id } = useParams();
return
<div>
</div>
);
<Route path="/blog">
<Route path=":category">
</Route>
</Route>
This setup defines a parent route /blog with child routes for
categories (/:category) and individual posts
(/:category/:postId). The CategoryPosts and PostDetails
components can handle fetching and displaying relevant
data based on the URL parameters.
Additional Considerations
Implementing Navigation
Links and Programmatic
Navigation
Building a single-page application (SPA) with React and
Spring Boot 3 often involves smooth navigation between
different pages. React Router provides a powerful library for
defining routes and handling navigation within your React
application. This guide explores essential concepts and code
examples for implementing navigation links and
programmatic navigation, inspired by "Mastering Full Stack
Development with Spring Boot 3 and React."
Bash
// App.js
function App()
return
<BrowserRouter>
<header>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</header>
<Routes>
</Routes>
</BrowserRouter>
export default App;
Explanation:
JavaScript
function MyComponent()
const handleButtonClick
};
return
<div>
<button onClick={handleButtonClick}>Go to
Products</button>
</div>
Explanation:
Additional Considerations
Testing Navigation:
1. Setting Up Routes:
JavaScript
// App.js
return
<BrowserRouter>
<Routes>
</Routes>
</BrowserRouter>
);
JavaScript
// ProductDetails.js
function ProductDetails()
const { id } = useParams();
return
<div>
</div>
);
Explanation:
JavaScript
// App.js
function App()
return
<BrowserRouter>
<Routes>
<Route path="/blog">
</Route>
</Route>
</Routes>
</BrowserRouter>
);
Explanation:
Additional Considerations
Project Setup
Bash
Bash
cd my-blog-spa
Component Structure
JavaScript
function App()
return
<BrowserRouter>
<Header />
<Routes>
</Route>
</Routes>
</BrowserRouter>
);
Explanation:
2. Header.js (Navigation):
JavaScript
import { Link } from 'react-router-dom';
function Header()
return
<header>
<Link to="/">Home</Link>
<Link to="/categories">Categories</Link>
</header>
);
Explanation:
JavaScript
return data;
};
function CategoryList()
useEffect
setCategories(fetchedCategories);
};
getCategories();
return
<div>
<h2>Categories</h2>
<ul>
{categories.map((category)
<li key={category.id}>
<Link to={`/categories/${category.slug}`}>
{category.name}</Link>
</li>
</ul>
</div>
);
Explanation:
JavaScript
// Replace with your Spring Boot 3 API call for fetching posts
by category
};
function PostList()
useEffect
setPosts(fetchedPosts);
};
getPosts();
},
[category]);
return
<div>
<ul>
{posts.map((post)
<li key={post.id}>
<Link to={`/posts/${post.id}`}>{post.title}
</Link>
</li>
</ul>
</div>
);
Explanation:
JavaScript
// Replace with your Spring Boot 3 API call for fetching a post
by ID
return data;
};
function PostDetails()
useEffect
setPost(fetchedPost);
};
getPost();
},
[postId]);
return
<div>
{post ?
<h2>{post.title}</h2>
<p>{post.content}</p>
<p>Loading post...</p>
</div>
);
export default PostDetails;
Explanation:
Bash
npm start
Additional Considerations:
JavaScript
function LoginForm()
event.preventDefault();
return
<form onSubmit={handleSubmit}>
<label htmlFor="username">Username:</label>
<input
type="text"
id="username"
name="username"
value={username}
};
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
name="password"
value={password}
};
<button type="submit">Login</button>
</form>
);
Explanation:
JavaScript
function LoginForm()
const validateForm
if (!username)
else if (!password)
setErrorMessage(error);
event.preventDefault();
if (validateForm()
return
<form onSubmit={handleSubmit}>
<button type="submit">Login</button>
</form>
Explanation:
JavaScript
event.preventDefault();
if (validateForm())
method: 'POST',
});
if (!response.ok)
console.log('Login successful!');
Explanation:
Java
@PostMapping("/api/login")
return ResponseEntity.ok(token);
Explanation:
Using Controlled
Components for Form State
Management
Forms are essential for gathering user input in web
applications. In React, controlled components offer a
powerful approach to manage form state, ensuring a
predictable and controllable user experience. This article
explores controlled components, their implementation with
hooks, and their integration with a Spring Boot 3 backend
for form submission.
JavaScript
import React, { useState } from 'react';
function LoginForm()
event.preventDefault();
return
<form onSubmit={handleSubmit}>
<label htmlFor="username">Username:</label>
<input
type="text"
id="username"
name="username"
value={username}
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
name="password"
value={password}
<button type="submit">Login</button>
</form>
Explanation:
JavaScript
function LoginForm() {
const validateForm
if (!username)
else if (!password)
setErrorMessage(error);
event.preventDefault();
if (validateForm())
// Send login data to Spring Boot backend (covered
later)
return
<form onSubmit={handleSubmit}>
<button type="submit">Login</button>
</form>
Explanation:
JavaScript
event.preventDefault();
if (validateForm())
method: 'POST',
});
if (!response.ok)
else
console.log('Login successful!');
Explanation:
Java
@PostMapping("/api/login")
String token =
generateJwtToken(loginRequest.getUsername());
return ResponseEntity.ok(token);
Explanation:
Additional Considerations
JavaScript
function RegistrationForm()
const validateForm
if (!username)
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
setErrorMessage(error);
}
const handleSubmit = (event)
event.preventDefault();
if (validateForm())
return
<form onSubmit={handleSubmit}>
<button type="submit">Register</button>
</form>
Explanation:
Java
@PostMapping("/register")
if (errors.isEmpty())
userService.saveUser(user);
return ResponseEntity.badRequest().body(String.join(",",
errors));
Explanation:
JavaScript
event.preventDefault();
if (validateForm())
});
if (!response.ok)
else
console.log('Registration successful!');
Explanation:
JavaScript
return
<div className="form-group">
<label htmlFor={name}>{label}</label>
<input
type={type}
id={name}
name={name}
value={value}
onChange={onChange}
</div>
);
JavaScript
function LoginForm()
event.preventDefault();
return
<form onSubmit={handleSubmit}>
<Input
label="Username:"
type="text"
name="username"
value={username}
<Input
label="Password:"
type="password"
name="password"
value={password}
<button type="submit">Login</button>
</form>
);
Explanation:
JavaScript
method: 'POST',
});
if (!response.ok)
else
console.log('Login successful!');
Explanation:
Java
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
List<Todo> getAllTodos();
Java
@RestController
@RequestMapping("/api/todos")
@Autowired
@GetMapping
return todoService.getAllTodos();
}
@PostMapping
return ResponseEntity.ok(todoService.saveTodo(todo));
JavaScript
return
<div className="todo">
<input
type="checkbox"
checked={todo.completed}
onChange={handleToggleCompleted}
<span className={todo.completed ? "completed" :
""}>{todo.description}</span>
<button onClick={handleDelete}>Delete</button>
</div>
);
Explanation:
JavaScript
event.preventDefault();
onSubmit(description);
return
<form onSubmit={handleSubmit}>
<input
type="text"
id="description"
name="description"
value={description}
<button type="submit">Create</button>
</form>
);
Explanation:
JavaScript
function TodoList()
useEffect
setIsLoading(true);
try
if (!response.ok)
setTodos(data);
catch (error)
setError(error.message);
finally
setIsLoading(false);
fetchTodos();
},
setIsLoading(true);
try
method: 'POST',
});
if (!response.ok)
setTodos([...todos, newTodo]);
catch (error)
setError(error.message);
finally
setIsLoading(false);
updatedTodo.completed = !updatedTodo.completed;
setIsLoading(true);
try
method: 'PUT',
body: JSON.stringify(updatedTodo),
});
if (!response.ok)
setTodos(
);
catch (error)
setError(error.message);
finally
setIsLoading(false);
setIsLoading(true);
try
method: 'DELETE',
});
if (!response.ok)
catch (error)
setError(error.message);
finally
setIsLoading(false);
return
<div>
<ul>
{todos.map((todo)
<Todo
key={todo.id}
todo={todo}
onToggleCompleted={handleToggleCompleted}
onDelete={handleDeleteTodo}
</ul>
<CreateTodoForm onSubmit={handleCreateTodo}
</div>
);
Explanation:
JavaScript
return
<div className="todo">
<input
type="checkbox"
checked={todo.completed}
onChange={handleToggleCompleted}
{isEditing ?
<input
type="text"
value={description}
<button onClick={handleDelete}>Delete</button>
</div>
Explanation:
Prerequisites:
1. Project Setup:
Spring Initializr:
● Visit https://start.spring.io/.
● Choose "Maven Project" or "Gradle Project" based
on your preference.
● Select "Java" as the language and a recent Spring
Boot version (e.g., 3.1.x).
Java
package com.example.demo.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
3. Repository Interface:
Java
package com.example.demo.repository;
import com.example.demo.model.Product;
import
org.springframework.data.jpa.repository.JpaRepository;
Java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplicati
on;
@SpringBootApplication
SpringApplication.run(DemoApplication.class, args);
5. RESTful Controller:
Java
package com.example.demo.controller;
import com.example.demo.model.Product;
import com.example.demo.repository.ProductRepository;
import
org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.
import java.util.List;
@RestController
@RequestMapping("/api/products")
@Autowired
@GetMapping
public List<Product> getAllProducts()
return productRepository.findAll();
@GetMapping("/{id}")
@PostMapping
return productRepository.save(product);
Explanation:
Properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
Java
@RunWith(SpringRunner.class)
@SpringBootTest
@Autowired
@Test
Project Setup:
Components:
API Calls:
Deployment:
1. Project Setup:
Bash
2. Dependencies:
We'll use Axios for making HTTP requests to our Spring Boot
API endpoints. Install it using npm or yarn:
Bash
cd product-app
3. Component Structure:
4. App.js:
JavaScript
function App()
const [products, setProducts] = useState;
useEffect
setProducts(response.data);
};
fetchProducts();
},
return
<div className="App">
<h1>Product List</h1>
<ProductList products={products}
</div>
);
Explanation:
5. ProductList.js:
JavaScript
return
<ul>
{products.map((product)
<li key={product.id}>
<Link to={`/products/${product.id}`}>
{product.name}</Link>
</li>
</ul>
);
Explanation:
JavaScript
const ProductDetails
const { id } = useParams();
useEffect
setProduct(response.data);
};
fetchProduct();
},
[id]);
return
<div>
{product ?
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
Explanation:
JavaScript
useEffect
if (productId)
setName(response.data.name);
setDescription(response.data.description);
setPrice(response.data.price);
};
fetchProduct();
},
[productId]);
e.preventDefault();
if (productId)
// Update product
await
axios.put(`http://localhost:8080/api/products/${productId}`,
productData);
else
setName('');
setDescription('');
setPrice(0);
};
return
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={(e)
=> setName(e.target.value)}
</label>
<label>
Description:
</label>
<label>
Price:
</label>
</form>
Explanation:
10. Deployment:
Understanding CORS:
Java
@RestController
@RequestMapping("/api/products")
// controller methods
Java
@Override
HttpServletResponse response =
(HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin",
"http://localhost:3000"); // Allowed origin
response.setHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE"); // Allowed methods
response.setHeader("Access-Control-Allow-Headers",
"Content-Type, Authorization"); // Allowed headers
chain.doFilter(req, res);
Java
@EnableWebMvc
@Override
source.addCorsMapping("/", new
CorsConfiguration().applyPermitDefaultValues());
@Bean
Best Practices:
Frontend (React):
// actions.js
type: 'FETCH_PRODUCTS',
});
// productList.js
products: state.products,
});
});
// productListContainer.js
useEffect
props.fetchProducts();
Java
@GetMapping("/api/products")
Advantages:
Considerations:
Frontend (React):
JavaScript
// productContext.js
products:
dispatch:
});
// productList.js
const ProductList
};
useEffect
fetchProducts();
},
Advantages:
Considerations:
Frontend (React):
JavaScript
// productList.js
};
Advantages:
Considerations:
Additional Considerations:
Error Handling:
Security:
Prerequisites:
Project Setup:
React Frontend:
● Utilize create-react-app to set up a new React
application.
● Install a library like Axios for making HTTP requests
to your Spring Boot API endpoints.
Component Structure:
JavaScript
// DataFetcher.jsx
useEffect
setIsLoading(true);
try
const response = await axios.get(url);
setData(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
[url]);
JavaScript
// DataDisplay.jsx
return
<div>
<div key={item.id}>
<p>{item.name}</p>
<p>{item.description}</p>
</div>
</div>
);
Data Flow:
Example Usage:
JavaScript
// App.js
const App
return
<div>
<DataFetcher url={API_URL}
<DataDisplay />
</div>
);
Additional Considerations:
Prerequisites:
Project Setup:
React Frontend:
Component Structure:
JavaScript
// DataForm.jsx
setFormData({formData, [event.target.name]:
event.target.value });
};
return
<form onSubmit={handleSubmit(handleFormSubmit)}>
<button type="submit">Submit</button>
</form>
);
JavaScript
// DataManager.jsx (Optional)
setData(formData);
};
return
);
Data Flow:
Example Usage:
JavaScript
// App.js
const App
try
catch (error)
console.error('Error creating data:', error);
return
<div>
</div>
);
JavaScript
try
catch (error)
Additional Considerations:
Prerequisites:
Project Setup:
React Frontend:
Component Structure:
JavaScript
// DataAccess.jsx
setIsLoading(true);
try
method,
url,
});
setData(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
useEffect
fetchData();
},
};
Read (GET):
JavaScript
// DataList.jsx
const DataList
return
<ul>
{data.map((item)
<li key={item.id}>{item.name}</li>
</ul>
);
Data Creation:
Create (POST):
JavaScript
// DataCreate.jsx
const DataCreate
setFormData({formData, [event.target.name]:
event.target.value });
event.preventDefault();
try
catch (error)
return
<form onSubmit={handleSubmit}>
<button type="submit">Create</button>
</form>
);
Data Editing:
Update (PUT):
JavaScript
// DataEdit.jsx (example)
const DataEdit = ({ id })
useEffect
setIsLoading(true);
try
setData(response.data);
catch (error)
setError(error);
finally
setIsLoading(false);
fetchData();
};
event.preventDefault();
try
};
Data Deletion:
Delete (DELETE):
Additional Considerations:
Understanding Errors:
Frontend Validation:
JavaScript
};
onSubmit(data);
return
<form onSubmit={handleSubmit(handleFormSubmit)}>
<input
type="email"
onChange={handleChange}
<button type="submit">Submit</button>
</form>
);
JavaScript
// (existing code)
setIsLoading(true);
try
method,
url,
});
setData(response.data);
catch (error)
setError({ message: 'Network Error' }); // Default error
message
if (error.response)
finally
setIsLoading(false);
// (existing code)
};
JavaScript
// ErrorBoundary.jsx
constructor(props)
super(props);
static getDerivedStateFromError(error)
componentDidCatch(error, errorInfo)
render()
if (this.state.hasError)
return this.props.children;
JavaScript
// (existing code)
const DataList
// (existing code)
};
JavaScript
const DataCreate
// (existing code)
event.preventDefault();
try
catch (error)
// (existing code)
JavaScript
return
<form onSubmit={handleSubmit(handleFormSubmit)}>
<input
type="email"
onChange={handleChange}
<button type="submit">Submit</button>
</form>
);
Project Setup:
User Model:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ManyToMany(fetch = FetchType.EAGER)
inverseJoinColumns = @JoinColumn(name =
"role_id"))
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
Java
@Autowired
@Override
User user =
userRepository.findByUsername(username);
if (user == null)
this.user = user;
Java
@Configuration
@EnableWebSecurity
@Bean
@Override
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/register").permitAll() // Allow
registration without authentication
.antMatchers("/api/auth/login").permitAll() // Allow
login without authentication
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtTokenFilter("/api/"),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().and()
.sessionManagement().sessionCreationOption(Ses
sionCreationPolicy.STATELESS)
}
@Override
@Bean
Frontend (React):
Project Setup:
Login Component:
Register Component:
Protected Routes:
Security Considerations:
Project Setup:
User Model:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ManyToMany(fetch = FetchType.EAGER)
inverseJoinColumns = @JoinColumn(name =
"role_id"))
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
@Autowired
@Override
User user =
userRepository.findByUsername(username);
if (user == null)
this.user = user;
Security Configuration:
Java
@Configuration
@EnableWebSecurity
@Autowired
@Override
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/register").permitAll() // Allow
registration without authentication
.antMatchers("/api/auth/login").permitAll() // Allow
login without authentication
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtTokenFilter("/api/"),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().and()
.sessionManagement().sessionCreationOption(Ses
sionCreationPolicy.STATELESS)
@Override
protected void configure(AuthenticationManagerBuilder
auth) throws Exception
{ auth.userDetailsService(userDetailsService).password
Encoder(passwordEncoder()
@Bean
Java
@PostMapping("/api/auth/login")
public ResponseEntity<LoginResponse>
login(@RequestBody LoginRequest loginRequest)
User user =
userRepository.findByUsername(loginRequest.getUsername(
);
if (user == null ||
!passwordEncoder.matches(loginRequest.getPassword(),
user.getPassword()
return ResponseEntity.badRequest().build();
Frontend (React):
Project Setup:
Login Component:
JavaScript
const LoginComponent
e.preventDefault();
try
localStorage.setItem('jwtToken', token);
catch (error)
console.error(error);
return
<form onSubmit={handleSubmit}>
<button type="submit">Login</button>
</form>
Protected Routes:
JavaScript
if (!token)
JavaScript
baseURL: 'http://localhost:8080/api',
headers:
Authorization: `Bearer
${localStorage.getItem('jwtToken')}`,
},
try
console.log(response.data);
catch (error)
console.error(error);
// Handle errors
Security Considerations:
● Use a strong secret key for JWT generation and
store it securely (e.g., environment variables).
● Implement HTTPS on both backend and frontend for
secure communication.
● Consider refresh tokens for longer-term
authentication sessions.
● Handle token expiration and refresh appropriately.
● Securely store the JWT token in the frontend (e.g.,
HttpOnly cookies with proper flags).
● Implement proper error handling and user feedback
for authentication failures and unauthorized access
attempts.
Implementing Authorization
Logic in Spring Boot
Controllers
This guide explores implementing authorization logic in
Spring Boot 3 controllers using Spring Security. We'll ensure
that only authorized users with specific roles can access
protected resources in your application.
Project Setup:
● Create a new Spring Boot project using Spring
Initializr.
● Include dependencies for Spring Security, Web, and
JPA/Hibernate for database interaction (consider
using a database like H2 for simplicity).
User Model:
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ManyToMany(fetch = FetchType.EAGER)
inverseJoinColumns = @JoinColumn(name =
"role_id"))
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Java
Java
@Override
User user =
userRepository.findByUsername(username);
if (user == null)
this.user = user;
Java
@Configuration
@EnableWebSecurity
@Autowired
@Bean
@Override
protected void configure(HttpSecurity http) throws
Exception
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/register").permitAll() // Allow
registration without authentication
.antMatchers("/api/auth/login").permitAll() // Allow
login without authentication
.antMatchers("/api/admin/").hasRole("ADMIN") //
Admin-specific endpoint
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtTokenFilter("/api/"),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().and()
.sessionManagement().sessionCreationOption(Ses
sionCreationPolicy.STATELESS)
@Override
@RestController
@RequestMapping("/api/products")
@GetMapping
@GetMapping("/{id}")
Frontend (React):
Error Handling:
Java
@Entity
@Id
private Long id;
@Enumerated(EnumType.STRING)
ADMIN,
USER
Java
@Entity
@Id
@ManyToMany(mappedBy = "roles")
Java
@EnableWebSecurity
@Override
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/admin/").hasRole("ADMIN")
.antMatchers("/").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.and()
.logout().logoutUrl("/logout");
@Override
Frontend (React):
JavaScript
user: null,
setUser:,
isAuthenticated: false,
setIsAuthenticated:,
});
useEffect
if (token)
method: 'POST',
localStorage.setItem('token', token);
};
const logout
localStorage.removeItem('token');
setUser(null);
setIsAuthenticated(false);
};
return
{children}
</AuthContext.Provider>
);
JavaScript
```javascript
return
<Router>
<Routes>
</Routes>
</Router>
);
Pros:
Cons:
Bash
Pros:
Cons:
Additional Considerations:
Further Exploration:
● Heroku Documentation:
https://devcenter.heroku.com/
● AWS Getting Started:
https://aws.amazon.com/getting-started/
Maven:
Gradle:
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Groovy
dependencies
implementation 'org.springframework.boot:spring-boot-
starter-web'
Bash
mvn package
Bash
./gradlew bootJar
XML
<packaging>war</packaging>
Groovy
archiveBaseName = 'your-app-name'
Deployment Considerations:
Deployment Strategies
Additional Resources:
● Spring Boot Packaging:
https://medium.com/@javadzone/spring-boot-
packaging-d63bf694cbb7
● Deploying Spring Boot Applications:
https://docs.spring.io/spring-boot/how-
to/deployment/index.html
● Building React Applications for Production:
https://create-react-app.dev/docs/production-build/
Configuring Deployment
Settings for React
Applications
Mastering full-stack development with Spring Boot 3 for the
backend and React for the frontend equips you to build
robust and interactive web applications. After packaging
your Spring Boot application (covered in the previous
section),it's time to configure your React application for
deployment. This involves optimizing the build process and
preparing it for a production environment.
Production Mode:
JavaScript
// webpack.config.js
module.exports
// other configurations
mode: 'production'
Source Maps:
JavaScript
// webpack.config.js
module.exports
// other configurations
devtool: 'source-map'
Static Asset Handling:
JavaScript
// webpack.config.js
module.exports
// other configurations
module:
rules:
test: /\.(png|jpg|gif|svg)$/,
use:
loader: 'file-loader',
options:
name: '[path][name].[ext]',
},
Environment Variables
JavaScript
// App.js
function MyComponent()
return
<div>
</div>
JavaScript
// webpack.config.js
module.exports
// other configurations
plugins:
new webpack.DefinePlugin
'process.env.REACT_APP_API_URL':
JSON.stringify('https://your-api-url.com'),
JavaScript
// App.js
function MyComponent()
useEffect
fetch(`${BASE_URL}/api/data`)
},
return
<div>
</div>
Deployment Considerations
Bash
# Using Webpack
# Using Parcel
Deployment:
On-Premise Servers:
Containerization (Docker):
Managing Database
Connections and
Environment Variables in
Production
Mastering full-stack development with Spring Boot 3 and
React equips you to build robust applications that interact
with databases. However, managing database connections
and environment variables securely in production is crucial
for application functionality and security. This guide explores
best practices for handling these elements during
deployment.
Configuration:
Java
@Configuration
@Value("${spring.datasource.url}")
@Value("${spring.datasource.username}")
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
dataSource.setJdbcUrl(dbUrl);
dataSource.setUsername(dbUsername);
dataSource.setPassword(dbPassword);
return dataSource;
Security Considerations:
Java
@Service
@Value("${api.key}")
Security Considerations:
Monitoring Strategies
Java
@SpringBootApplication
@EnableAdminServer
Logging:
XML
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
Alerting:
Regular Updates:
XML
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.0.12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Security Patching:
Adding Dependencies:
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
</dependencies>
Groovy
// Gradle
dependencies
testImplementation 'org.springframework.boot:spring-
boot-starter-test'
testImplementation 'org.mockito:mockito-core'
Mocking Dependencies:
Java
@SpringBootTest
@Autowired
@MockBean
@Test
assertThat(response.getStatusCode().isEqualTo(HttpStatu
s.OK);
assertThat(response.getBod().isEqualTo(dataObject);
Java
@SpringBootTest
@Autowired
@Test
.andExpect(status().isOk())
.andReturn();
String content =
result.getResponse().getContentAsString();
assertThat(user.getId()).isEqualTo(userId);
Java
@Test
assertThat(discountedPrice).isEqualTo(90.0);
Java
@SpringBootTest
@Autowired
@MockBean
@Test
when(mockRepository.save(data)).thenReturn(data);
service.saveData(data);
verify(mockRepository).save(data);
Best Practices
Adding Dependencies:
JSON
"devDependencies":
"jest": "^28.0.0",
"@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^13.3.9",
"@testing-library/user-event": "^14.1.0"
Rendering Components:
JavaScript
expect(titleElement).toBeInTheDocument();
Finding Elements:
JavaScript
test('Clicking the button triggers the click handler',
render(<MyComponent onClick={mockClickHandler});
userEvent.click(button);
expect(mockClickHandler).toHaveBeenCalledTimes(1);
JavaScript
render(<MyComponent onSubmit=
{mockSubmitHandler});
userEvent.type(screen.getByLabelText('Name'), 'John
Doe');
userEvent.submit(form);
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
Assertions:
Use Jest's expect assertions to verify the state of your
component after rendering or interacting with it. This helps
ensure the component behaves as expected based on
different inputs.
Accessibility Testing:
JavaScript
render(<MyComponent >);
Best Practices
JavaScript
describe('User Login',
cy.visit('/login');
cy.get('#username').type('john.doe');
cy.get('#password').type('secret123');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
JavaScript
await page.goto('http://localhost:3000/');
await page.fill('#search-input', 'laptop');
await page.click('#search-button');
await page.waitForSelector('.product-card');
expect(products.length).toBeGreaterThan(0);
};
Security Considerations:
Importance of Test-Driven
Development (TDD) in Full
Stack Projects
Mastering full-stack development with Spring Boot 3 and
React requires a strategic approach to building robust and
maintainable applications. Test-Driven Development (TDD)
emerges as a powerful methodology that emphasizes
writing tests before writing actual code. This guide explores
the importance of TDD in full-stack projects, showcasing its
benefits and implementation with Spring Boot 3 for the
backend and React for the frontend.
Understanding TDD
Java
@Test
assertThat(discountedPrice).isEqualTo(90.0);
JavaScript
expect(productNameElement).toBeInTheDocument;
Overcoming Challenges
1. Leverage Caching:
● Implement caching mechanisms like Ehcache,
Redis, or Hazelcast to store frequently accessed
data in memory.This reduces database load and
improves response times for repeated requests.
Java
@SpringBootApplication
@EnableCaching
@Bean
4. Minimize Logging:
5. Optimize Configuration:
Java
@Service
@Async
GET http://localhost:8080/actuator/health
Understanding Caching
1. Cache-Aside Pattern:
Java
@Service
@Cacheable(cacheNames = "products")
return product;
2. Write-Through Pattern:
In this approach, any updates to the data source (e.g.,
database) automatically update the corresponding entry in
the cache.This ensures consistency between the cache and
the source. However, it can introduce additional overhead
for write operations.
3. Write-Behind Pattern:
4. Cache Expiration:
Java
@Configuration
@EnableCaching
@Override
cacheManager.setCacheManager(simpleCacheManager);
5. Caching Libraries:
Additional Considerations:
Java
@SpringBootApplication
@EnableDiscoveryClient
SpringApplication.run(GatewayApplication.class, args);
@Bean
return builder.routes()
.path("/uses/)
.uri("lb://user-service") // Load balancer URI with
service name
.build();
JavaScript
function UserList()
useEffect
setUsers(data);
};
fetchData();
},
return
<div>
<h2>Users</h2>
<ul>
{users.map((user)
<li key={user.id}>{user.name}</li>
</ul>
</div>
);
The React component fetches user data from the root path
("/users") of the backend, which is handled by the Spring
Cloud Gateway and subsequently routed to an appropriate
instance of the "user-service".
JavaScript
const MyPureComponent = React.memo((props)
JavaScript
shouldComponentUpdate(nextProps)
render
// Render logic
JavaScript
const MyComponent
JavaScript
function MyComponent()
JavaScript
const MyComponent
const updateData
setData((draft)
draft.value += 1;
});
JavaScript
function MyComponent()
return
<div>
</div>
React.lazy and Suspense: Combine React.lazy with
Suspense to handle the loading state while lazy-loaded
components are being fetched. This provides a smoother
user experience.
JavaScript
function MyComponent
return
<div>
<Suspense fallback={<div>Loading...</div>
<MyLazyComponent
</Suspense>
</div>
JavaScript
import { debounce } from 'lodash';
500);
function MyComponent
```jsx
function MyComponent
useEffect
if (!data)
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Java
Java
@RestController
@RequestMapping("/api/tasks")
@Autowired
@GetMapping
return taskRepository.findAll;
@PostMapping
public Task createTask(@RequestBody Task task)
return taskRepository.save(task);
Frontend (React):
JavaScript
const TaskList
useEffect
};
fetchTasks();
},
Java
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Entity
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@OneToMany(mappedBy = "cart")
Java
@RestController
@RequestMapping("/api/products")
@Autowired
@GetMapping
return productService.getAllProducts();
}
Frontend (React):
JavaScript
return
<div className="product-card">
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
};
export default ProductCard;
Further Considerations:
Frontend (React):
JavaScript
type: 'ADD_TO_CART',
payload: product
JavaScript
items:
addItem(product)
this.items.push(product);
const ProductCard
<Formik
validate={(values)
const errors
return errors;
};
onSubmit={onSubmit}
<form onSubmit={handleSubmit}>
</Formik>
JavaScript
const App
return
<Router>
<Routes>
</Routes>
</Router>
Java
@RepositoryRestResource(collectionResourceRel =
"products", path = "products")
Java
List<Product> discountedProducts =
productRepository.findAll(product.price.lt(10.0));
Java
@Configuration
@EnableWebSecurity
@Override
protected void configure(HttpSecurity http) throws
Exception
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.anyRequest().authenticated()
.addFilterBefore(new
JwtAuthenticationFilter(userDetailsService),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtAuthorizationFilter(),
UsernamePasswordAuthenticationFilter.class);
@Bean
public UserDetailsService
userDetailsService(PasswordEncoder passwordEncoder)
return username
Additional Considerations:
Understanding CI/CD
Benefits of CI/CD
YAML
version: 2.1
jobs:
build:
docker:
- image: circleci/openjdk:17-jdk-headless
steps:
- checkout
- run: npm install && npm test # Install and run React
tests
- store_artifacts:
3. Additional Considerations:
Here's an example:
Java
@SpringBootApplication
SpringApplication.run(MySpringBootApp.class, args);
Java
@Configuration
@Bean
Java
@Configuration
@Bean
dataSource.setUrl("jdbc:h2:mem:testdb");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
Java
@SpringBootApplication
@ComponentScan(basePackages =
"com.example.myapp.controllers")
SpringApplication.run(MySpringBootApp.class, args);
Java
@RestController
@RequestMapping("/api/v1/products")
public class ProductController
@Autowired
@GetMapping
return productService.findAllProducts();
Here's an example:
Java
@Configuration
@PropertySource("classpath:application.properties")
@Value("${app.name}")
return appName;
Java
@Service
@Autowired
ResponseEntity<String> response =
restTemplate.getForEntity(url, String.class);
return response.getBody();
Here's an example:
JavaScript
function Counter()
const increment
setCount(count + 1);
};
return
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
JavaScript
function UserList()
useEffect
fetch('https://api.example.com/users')
},
return
<ul>
{users.map(user
<li key={user.id}>{user.name}</li>
</ul>
This UserList component uses useEffect to fetch user data
from an API on component mount (indicated by the empty
dependency array). The fetched data is then used to
populate the user list.
The useContext hook allows you to access and use the state
from a React Context object within a functional component.
This is useful for sharing state across a large component
tree without passing props through multiple levels.
JavaScript
function MyComponent()
return
<div>
</div>
JavaScript
function InputField
const focusInput
inputRef.current.focus();
};
return
<div>
</div>
Code Example
Java
@RestController
@RequestMapping("/api/products")
@Autowired
@GetMapping
return productService.findAllProducts();
JavaScript
useEffect
try
setProducts(response.data);
catch (error)
console.error(error);
};
fetchData();
},
return
<ul>
{products.map(product
<li key={product.id}>{product.name}</li>
</ul>
);
Setting Up a Continuous
Integration Pipeline with
Tools Like Jenkins or GitHub
Actions
Continuous Integration (CI) is a crucial practice in modern
software development, allowing for automated testing and
building upon code changes. This ensures early detection of
issues and maintains code quality. Let's explore setting up a
CI pipeline for a Full Stack application built with Spring Boot
3 and React using Jenkins and GitHub Actions.
Both Jenkins and GitHub Actions are popular CI/CD tools, but
they cater to different needs. Here's a quick comparison:
Groovy
pipeline
agent any
stages
stage('Checkout Code')
steps
steps
steps
script
sh 'npm install'
stage('Run Tests')
steps
YAML
on:
push:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
uses: actions/setup-node@v3
with:
with:
● Run Tests:
● YAML
uses: maven/maven-action@v3
with:
goals: test
YAML
uses: aws-actions/aws-s3-deploy@v2
with:
aws-bucket: your-bucket-name
region: your-aws-region
secret-access-key: ${{
secrets.AWS_SECRET_ACCESS_KEY }}