Using Collections in Microservices and REST APIs – Best Practices for Java Developers

Illustration for Using Collections in Microservices and REST APIs – Best Practices for Java Developers
By Last updated:

When designing microservices and REST APIs in Java, one of the most overlooked but critical aspects is how collections like List, Set, and Map are used in request and response payloads. Poor handling can lead to serialization errors, performance bottlenecks, or even security issues.

This tutorial explores how to effectively use Java Collections in microservices and REST APIs — including DTO design, immutability, serialization tips, and defensive copying. Whether you're building APIs with Spring Boot, Jakarta EE, or Quarkus, these practices are universally applicable.


Why Collections Matter in APIs

  • Used to represent lists of data (e.g., products, users, orders)
  • Form part of request bodies, response models, and query parameters
  • Impact serialization, immutability, null handling, and API clarity

Choosing the Right Collection

Collection Type Best Used For Notes
List Ordered data, duplicates allowed Common in JSON arrays
Set Unique values, no duplicates Requires special handling for JSON
Map Key-value pairs Nested JSON or configs

Java DTO Design Best Practices

Use Concrete Types in DTOs

public class ProductResponse {
    private List<ProductDTO> products; // ✅ Good
}

Avoid:

private Collection<ProductDTO> products; // ❌ Avoid abstract types in DTOs

Default to Empty Collections

private List<String> tags = new ArrayList<>(); // Avoids null in JSON

Use Immutable Collections in DTOs (Java 10+)

private List<String> categories = List.of(); // ✅ Safe, immutable

Serialization Considerations

Jackson and Collections

  • Jackson serializes List, Set, and Map out of the box.
  • Set may serialize out of order (unordered by definition).
  • Map keys must be string-convertible for JSON.

Avoid Null Collections

// Bad
{ "items": null }

// Good
{ "items": [] }

Use annotations or defaults:

@JsonInclude(Include.NON_NULL)
private List<String> items = new ArrayList<>();

Or initialize with Collections.emptyList() / List.of().


Defensive Copying in Microservices

Why It Matters

  • Prevents external modification of shared or cached data
  • Promotes immutability and thread safety
  • Avoids bugs due to shared references

Example

public class OrderDTO {
    private final List<ItemDTO> items;

    public OrderDTO(List<ItemDTO> items) {
        this.items = List.copyOf(items); // Java 10+ defensive copy
    }

    public List<ItemDTO> getItems() {
        return items;
    }
}

Functional Programming and APIs

Filtering Collections Before Responding

List<UserDTO> activeUsers = allUsers.stream()
    .filter(UserDTO::isActive)
    .collect(Collectors.toList());

Avoid Exposing Internal Collections

Always return new list copies or unmodifiable views in service layers:

return Collections.unmodifiableList(userList);

Java Version Tracker

📌 What's New in Java?

  • Java 8
    • Streams, functional mapping for pre-API filtering
  • Java 9
    • List.of(), Map.of() — immutable and null-safe
  • Java 10
    • List.copyOf() and var for DTO simplification
  • Java 21
    • Structured concurrency encourages immutability in concurrent API calls

REST API Design Tips

Tip Why It Matters
Return empty lists, not nulls Cleaner JSON, avoids null-pointer issues
Use List over Set in payloads JSON preserves order, Set doesn’t
Keep collections immutable if possible Ensures thread safety and API contract
Validate incoming collections Prevents overflows and injection
Paginate large collections Reduces payload size and improves performance

Common Pitfalls

  • Exposing mutable collections from services or controllers
  • Using Set in JSON APIs without guaranteeing order
  • Accepting raw List<Object> in endpoints — causes serialization issues
  • Returning null for empty lists — breaks frontend parsers

Refactoring Legacy APIs

  • Replace raw arrays or null returns with List.of() or Collections.emptyList()
  • Wrap collections in List.copyOf() before returning them from services
  • Introduce DTOs to encapsulate collection responses

Real-World Use Cases

  • E-commerce APIs – Return product lists, cart items, user orders
  • Banking apps – Display transaction history, beneficiaries
  • SaaS platforms – Retrieve team members, notifications, audit logs
  • Analytics dashboards – Aggregate and group time-series metrics

Conclusion and Key Takeaways

  • Collections are integral to REST API payloads and microservice boundaries
  • Prefer List for ordered and predictable JSON responses
  • Use immutable or defensive copied collections in DTOs
  • Always avoid exposing mutable internals in controller responses
  • Choose the right collection based on business needs and API clarity

FAQ – Collections in Microservices and REST APIs

  1. Should I use List or Set in REST responses?
    Use List — it preserves order and is more JSON-friendly.

  2. How do I make my collection immutable in a DTO?
    Use List.copyOf() or List.of() (Java 10+).

  3. Is it okay to return null for a collection field?
    No — return empty lists instead to avoid JSON parsing issues.

  4. How to avoid serialization errors with Map?
    Ensure keys are String or convertible to string in JSON.

  5. Can I expose Set in Spring Boot controller?
    Yes, but it's better to convert to List for predictable JSON.

  6. What’s the best practice for incoming POST request collections?
    Validate size, non-null elements, and duplication if using Set.

  7. Should service layers return collections directly?
    Return immutable or defensive copies to prevent external mutation.

  8. Are Java collections thread-safe in APIs?
    No — collections must be handled carefully for concurrency.

  9. How can I sort a collection before returning from an API?
    Use stream().sorted() or Collections.sort() before serialization.

  10. Do Java Streams serialize in JSON?
    No — collect them into a list before returning.