DEV Community

realNameHidden
realNameHidden

Posted on

How do you handle optional fields in request body in Spring Boot?

Introduction

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"
}
Enter fullscreen mode Exit fullscreen mode

Here:

  • username, password → required
  • description → 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
}'
Enter fullscreen mode Exit fullscreen mode

✅ Response

User created successfully. username=sample_user, description=No description provided, notificationsEnabled=false
Enter fullscreen mode Exit fullscreen mode

❌ Request (Missing Required Field)

curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{
  "username": "sample_user"
}'
Enter fullscreen mode Exit fullscreen mode

❌ Response

{
  "status": 400,
  "error": "Bad Request",
  "message": "Password is required"
}
Enter fullscreen mode Exit fullscreen mode

✅ Example 2: Optional Fields Using Optional<T> (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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Controller

package com.example.demo.controller;

import com.example.demo.dto.UpdatePreferencesRequest;
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";
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
}'
Enter fullscreen mode Exit fullscreen mode

✅ Response

Preferences updated successfully
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Validate only required fields
    Avoid marking optional fields with validation constraints.

  2. Provide sensible defaults
    Prevent unnecessary client-side work.

  3. Use Optional<T> mainly for partial updates
    Ideal for PATCH, not for every DTO.

  4. Design APIs for backward compatibility
    Optional fields allow safe evolution.

  5. Handle null values 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
  • Optional<T> where appropriate

you can build APIs that are flexible, robust, and beginner-friendly.

Mastering handling optional fields in request body 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!


🔗 Helpful Resources


Enter fullscreen mode Exit fullscreen mode

Top comments (0)