How do you handle optional fields in request body in Spring Boot?
Source: Dev.to
Think about filling out a signup form online.
Some fields like username and password are mandatory, while others like profile description or preferences are optional.
You wouldn’t expect the form to fail just because you skipped an optional field—right?
The same idea applies to REST APIs. When a client sends a request body, not every field should be required. Knowing how to handle optional fields in request body in Spring Boot is essential for building flexible, backward‑compatible, and beginner‑friendly APIs.
In this blog, you’ll learn this concept step by step, using simple language, real‑world analogies, and complete end‑to‑end examples with cURL requests and responses.
Core Concepts
What Are Optional Fields in a Request Body?
Optional fields are JSON properties that:
- May or may not be present in the request
- Should not break the API if missing
- Often have default values or conditional logic
Example request body
{
"username": "sample_user",
"password": "secret123",
"description": "Loves backend development"
}
Here:
username,password→ requireddescription→ optional
🧠 Analogy
A request body is like a form:
- Required fields = mandatory questions
- Optional fields = extra details you may skip
How Spring Boot Handles Missing Fields
Spring Boot (via Jackson):
- Maps JSON to Java objects automatically
- Sets missing fields to
null - Allows default values and validation annotations
No manual parsing is needed.
Common Use Cases
- ✅ Partial data submission
- ✅ Backward compatibility
- ✅ Optional user preferences
- ✅ Feature toggles
- ✅ Evolving APIs
Code Examples (End‑to‑End)
✅ Example 1: Optional Fields with Validation and Default Values
Use Case
Create a user where some fields are optional.
Step 1 – Request DTO
package com.example.demo.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class CreateUserRequest {
@NotBlank(message = "Username is required")
private String username;
@NotBlank(message = "Password is required")
@Size(min = 6, message = "Password must be at least 6 characters")
private String password;
// Optional field
private String description;
// Optional field with default value
private boolean notificationsEnabled = false;
// Getters and setters
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isNotificationsEnabled() {
return notificationsEnabled;
}
public void setNotificationsEnabled(boolean notificationsEnabled) {
this.notificationsEnabled = notificationsEnabled;
}
}
Step 2 – REST Controller
package com.example.demo.controller;
import com.example.demo.dto.CreateUserRequest;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public String createUser(@Valid @RequestBody CreateUserRequest request) {
String description = request.getDescription() != null
? request.getDescription()
: "No description provided";
return "User created successfully. " +
"username=" + request.getUsername() +
", description=" + description +
", notificationsEnabled=" + request.isNotificationsEnabled();
}
}
Step 3 – Test with cURL
✅ Request (Optional Fields Missing)
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{
"username": "sample_user",
"password": "secret123"
}'
✅ Response
User created successfully. username=sample_user, description=No description provided, notificationsEnabled=false
❌ Request (Missing Required Field)
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{
"username": "sample_user"
}'
❌ Response
{
"status": 400,
"error": "Bad Request",
"message": "Password is required"
}
✅ Example 2: Optional Fields Using Optional (Best for PATCH)
Use Case
Update user preferences partially.
Step 1 – Request DTO
package com.example.demo.dto;
import java.util.Optional;
public class UpdatePreferencesRequest {
private Optional<String> theme = Optional.empty();
private Optional<Boolean> notificationsEnabled = Optional.empty();
public Optional<String> getTheme() {
return theme;
}
public void setTheme(Optional<String> theme) {
this.theme = theme;
}
public Optional<Boolean> getNotificationsEnabled() {
return notificationsEnabled;
}
public void setNotificationsEnabled(Optional<Boolean> notificationsEnabled) {
this.notificationsEnabled = notificationsEnabled;
}
}
Step 2 – Controller
package com.example.demo.controller;
import com.example.demo.dto.UpdatePreferencesRequest;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserPreferenceController {
@PatchMapping("/{id}/preferences")
public String updatePreferences(@PathVariable Long id,
@RequestBody UpdatePreferencesRequest request) {
// Simulated existing user data
String currentTheme = "light";
boolean currentNotifications = true;
// Apply updates only if present
String newTheme = request.getTheme().orElse(currentTheme);
boolean newNotifications = request.getNotificationsEnabled()
.orElse(currentNotifications);
// In a real app you would persist the changes here
return "Preferences updated for user " + id +
". theme=" + newTheme +
", notificationsEnabled=" + newNotifications;
}
}
Step 3 – Test with cURL
✅ Partial Update (only theme)
curl -X PATCH http://localhost:8080/users/42/preferences \
-H "Content-Type: application/json" \
-d '{"theme":"dark"}'
✅ Response
Preferences updated for user 42. theme=dark, notificationsEnabled=true
✅ Partial Update (only notifications flag)
curl -X PATCH http://localhost:8080/users/42/preferences \
-H "Content-Type: application/json" \
-d '{"notificationsEnabled":false}'
✅ Response
Preferences updated for user 42. theme=light, notificationsEnabled=false
Takeaways
- Optional fields let APIs evolve without breaking existing clients.
- Spring Boot (Jackson) automatically maps missing JSON properties to
nullor default values. - Use validation annotations (
@NotBlank,@Size, etc.) for required fields. - For partial updates (
PATCH), wrapping fields inOptionalmakes intent explicit and avoidsnull‑checking boilerplate.
By applying these patterns, you’ll build APIs that are both robust and developer‑friendly. Happy coding!
package com.example.demo.dto;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/preferences")
public class PreferencesController {
@PatchMapping
public String updatePreferences(@RequestBody UpdatePreferencesRequest request) {
request.getTheme()
.ifPresent(t -> System.out.println("Updating theme to: " + t));
request.getNotificationsEnabled()
.ifPresent(n -> System.out.println("Updating notifications: " + n));
return "Preferences updated successfully";
}
}
Step 3: Test with cURL
✅ Request (Only One Field Provided)
curl -X PATCH http://localhost:8080/preferences \
-H "Content-Type: application/json" \
-d '{
"theme": "dark"
}'
✅ Response
Preferences updated successfully
Best Practices
- Validate only required fields – avoid marking optional fields with validation constraints.
- Provide sensible defaults – prevent unnecessary client‑side work.
- Use
Optionalmainly for partial updates – ideal forPATCH, not for every DTO. - Design APIs for backward compatibility – optional fields allow safe evolution.
- Handle
nullvalues explicitly – never assume optional fields are always present.
Conclusion
Handling optional fields correctly is a core API‑design skill in Spring Boot. By using:
- Validation annotations
- Default values
Optionalwhere appropriate
you can build APIs that are flexible, robust, and beginner‑friendly.
Mastering handling optional fields in request bodies in Spring Boot will strengthen your Java programming fundamentals and help you learn Java with real‑world backend patterns.
Call to Action
💬 Have questions about optional fields or request validation?
👇 Drop them in the comments below!