Sitemap
Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.

⚙️ Dynamic Request Bodies in Spring Boot — With One Clean, Reusable Endpoint

4 min readOct 28, 2025

--

“Can we send a different JSON structure depending on the use case?”

That’s the moment every backend developer realizes — static DTOs don’t scale.

Instead of writing 10 different controllers, I’ll show you how to handle dynamic JSON bodies cleanly in one endpoint — with full validation and zero chaos.

💡 The Problem: When DTOs Become a Nightmare

In a perfect world, every request has a well-defined DTO:

public class UserRequest {
private String name;
private String email;
}

But then product says:

“We’ll add admin registration, and each role has different fields.”

So you end up writing:

UserRequest
AdminRequest
GuestRequest
PartnerRequest

And your controller starts looking like this 😵‍💫:

@PostMapping("/user") ...
@PostMapping("/admin") ...
@PostMapping("/guest") ...

That’s rigid, repetitive, and impossible to maintain.

🚀 The Solution: Dynamic Request Handling

Instead of hardcoding DTOs, we’ll make Spring Boot accept dynamic JSON structures — and still keep type safety and validation.

Get Lakshika’s stories in your inbox

Join Medium for free to get updates from this writer.

We’ll use:

  • @RequestBody Map<String, Object> for flexible payloads
  • Optional: Jackson’s JsonNode for structured but dynamic parsing
  • Optional: Polymorphic DTOs for cleaner deserialization

🧩 Step 1: Accepting Flexible JSON with Map<String, Object>

@RestController
@RequestMapping("/api/register")
public class RegistrationController {
@PostMapping
public ResponseEntity<String> register(@RequestBody Map<String, Object> payload) {
String userType = (String) payload.get("type");
switch (userType) {
case "admin" -> handleAdmin(payload);
case "user" -> handleUser(payload);
default -> throw new IllegalArgumentException("Unknown type: " + userType);
}
return ResponseEntity.ok("Registered successfully ✅");
}
private void handleAdmin(Map<String, Object> payload) {
System.out.println("Admin payload: " + payload);
}
private void handleUser(Map<String, Object> payload) {
System.out.println("User payload: " + payload);
}
}

🧠 Result:
Now you can send any JSON body:

{
"type": "admin",
"email": "root@example.com",
"permissions": ["READ", "WRITE"]
}

or

{
"type": "user",
"name": "Alice",
"referralCode": "XYZ"
}

…and it works with a single endpoint.

⚙️ Step 2: Using JsonNode for Structured Dynamic Access

For better control, use Jackson’s JsonNode:

@PostMapping("/json")
public ResponseEntity<String> registerWithJson(@RequestBody JsonNode node) {
String type = node.path("type").asText();
if ("admin".equals(type)) {
JsonNode permissions = node.path("permissions");
System.out.println("Admin permissions: " + permissions);
}
return ResponseEntity.ok("Processed " + type);
}

💡 Why it’s great:

  • You get type-safe access (asText(), asInt(), etc.)
  • No need for predefined POJOs
  • Perfect for third-party webhooks or configurable forms

🧠 Step 3: Polymorphic DTOs — Type Safety + Flexibility

For cleaner code and validation, define type-based DTOs with Jackson polymorphism.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = AdminRequest.class, name = "admin"),
@JsonSubTypes.Type(value = UserRequest.class, name = "user")
})
public abstract class BaseRequest {
public String type;
}
@Data
class AdminRequest extends BaseRequest {
private List<String> permissions;
}
@Data
class UserRequest extends BaseRequest {
private String name;
private String referralCode;
}

Then your controller can stay clean:

@PostMapping("/dynamic")
public ResponseEntity<String> register(@RequestBody BaseRequest request) {
if (request instanceof AdminRequest admin) {
System.out.println("Admin: " + admin.getPermissions());
} else if (request instanceof UserRequest user) {
System.out.println("User: " + user.getName());
}
return ResponseEntity.ok("Processed " + request.getType());
}

✅ One endpoint
✅ Type-safe polymorphism
✅ Automatic deserialization

🧩 Step 4: Add Runtime Validation

Use @Valid + @JsonTypeInfo for runtime validation:

@PostMapping("/validate")
public ResponseEntity<String> registerValidated(@Valid @RequestBody BaseRequest request) {
return ResponseEntity.ok("Valid request for type: " + request.getType());
}

You can annotate subclasses normally:

@Data
class UserRequest extends BaseRequest {
@NotBlank
private String name;
}

If the request is invalid, Spring Boot automatically returns a 400 Bad Request with validation errors. 💥

🧠 Step 5: Bonus — Dynamic Fields in Entity Mapping

If you store flexible JSON in your DB (like PostgreSQL jsonb), map it like this:

@Entity
@Table(name = "dynamic_forms")
public class DynamicForm {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String type;
@Column(columnDefinition = "jsonb")
@Convert(converter = JsonNodeConverter.class)
private JsonNode data;
}

With a converter:

@Converter
public class JsonNodeConverter implements AttributeConverter<JsonNode, String> {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(JsonNode attribute) {
try {
return mapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@Override
public JsonNode convertToEntityAttribute(String dbData) {
try {
return mapper.readTree(dbData);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Now you can save any flexible JSON dynamically!

📊 Performance Tip

If you use dynamic payloads heavily:

  • Use JSONB indexes (PostgreSQL)
  • Cache parsed nodes to avoid repeated deserialization
  • Validate schema at API gateway layer for better control

🧠 TL;DR

💬 Final Thoughts

Dynamic request bodies don’t have to mean chaotic code.
With Spring Boot’s flexible deserialization and a bit of Jackson magic, you can build smart, scalable APIs that handle multiple payloads — without creating a DTO jungle 🌴.

So next time your frontend team says:

“We might send a few different fields…”

You just smile and say:

“Send whatever you want — I’ve got it covered 😉.”

A message from our Founder

Hey, Sunil here. I wanted to take a moment to thank you for reading until the end and for being a part of this community.

Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don’t receive any funding, we do this to support the community. ❤️

If you want to show some love, please take a moment to follow me on LinkedIn, TikTok, Instagram. You can also subscribe to our weekly newsletter.

And before you go, don’t forget to clap and follow the writer️!

--

Lakshika
Lakshika

Written by Lakshika

Spring Boot Developer | Writing about Java, Backend Architecture & Real-World System Design

Stackademic
Stackademic

Published in Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.