Spring Boot Large Batch Request Body? 6 Optimization Strategies

This article presents six optimization strategies for handling oversized batch request bodies in Spring Boot, covering network compression with GZIP, Protobuf adoption, streaming JSON parsing with Jackson JsonParser, early validation layers, asynchronous processing via Kafka, and request chunking to reduce memory pressure and improve throughput.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Boot Large Batch Request Body? 6 Optimization Strategies

Introduction

When handling batch requests like @RequestBody List<Product>, Spring Boot deserializes the entire JSON payload into Java objects before invoking the controller. For large payloads (e.g., 10,000 products with nested objects), this allocates tens of thousands of objects, increasing heap pressure, garbage collection, processing latency, memory consumption under concurrency, timeout risk, and reducing throughput as request threads are occupied longer.

Network Transmission Optimization

Optimization 1: Enable HTTP Compression

GZIP compression can significantly reduce repetitive JSON payload size (e.g., 15 MB to ~2 MB). Enable response compression via configuration:

server:
  compression:
    enabled: true
    mime-types:
      - application/json
      - text/plain
      - text/html
    min-response-size: 1KB

This compresses eligible HTTP responses. However, it does not enable decompression of incoming compressed request bodies. If clients send compressed requests, the application must explicitly support request decompression, with limits on decompressed body size to prevent decompression bombs. Compression adds CPU overhead and does not eliminate JSON-to-Java object conversion costs.

Optimization 2: Use Protobuf Instead of JSON

For internal service-to-service communication, Protocol Buffers (Protobuf) offers compact binary encoding with a schema. Example schema:

syntax = "proto3";

message ProductListRequest {
  repeated Product products = 1;
}

message Product {
  int64 id = 1;        // corresponds to Long id
  string name = 2;     // corresponds to String name
  string price = 3;    // corresponds to BigDecimal price
  int32 stock = 4;     // corresponds to Integer stock
}

Protobuf reduces payload size and parsing overhead for suitable workloads. A comparison image (JSON vs GZIP vs Protobuf) illustrates the differences.

JSON, GZIP, Protobuf size comparison
JSON, GZIP, Protobuf size comparison

Parsing Optimization

Solution: Streaming Processing

Jackson's JsonParser provides a low-level streaming API. Instead of building the entire list upfront, you can read the top-level JSON array element by element:

private final ObjectMapper objectMapper;

@PostMapping
public ResponseEntity<Object> processBulkProducts(HttpServletRequest request) throws IOException {
    int processed = 0;
    int failed = 0;
    try (JsonParser parser = objectMapper.getFactory().createParser(request.getInputStream())) {
        // 1. Validate and enter root array
        if (parser.nextToken() != JsonToken.START_ARRAY) {
            throw new IllegalArgumentException("Expected a JSON array");
        }
        // 2. Iterate over each Product object in the array
        while (parser.nextToken() != JsonToken.END_ARRAY) {
            // Deserialize current token to Product object
            Product product = objectMapper.readValue(parser, Product.class);
            try {
                // process product
                processed++;
            } catch (Exception ex) {
                failed++;
            }
        }
    }
    return ResponseEntity.ok("success");
}

With streaming, one Product is deserialized, processed, and its reference released before the next, drastically reducing peak memory usage.

Validation Optimization

Validation must be layered and executed early to avoid exhausting resources before detecting invalid input. At the entry point, limit request size, content type, decompressed volume, nesting depth, and batch count. At the business layer, validate fields and rules. Order matters: early interception avoids unnecessary deserialization and computation. Note that Content-Length is unreliable; actual bytes read must be limited.

Enforce Batch Size Limits

For traditional batch endpoints, Bean Validation can enforce collection limits after deserialization:

public class BulkProductRequest {
    @NotEmpty(message = "Product list cannot be empty")
    @Size(max = 1000, message = "Single batch import supports at most 1000 products")
    @Valid
    private List<Product> products;

    public List<Product> getProducts() {
        return products;
    }
    public void setProducts(List<Product> products) {
        this.products = products;
    }
}

Controller uses @Valid to trigger validation:

@PostMapping
public ResponseEntity<BulkResult> processBulk(
        @RequestBody @Valid BulkProductRequest request) {
    // ...
}

This prevents oversized batches from reaching the business layer but does not stop object creation during deserialization (the entire JSON is parsed and converted first). For truly massive payloads, enforce size limits before deserialization and use streaming parsing to validate elements as they arrive.

Architecture Optimization

Sometimes the biggest performance gain comes from changing the API contract, not the parser. Ask: does the client really need to wait for all 10,000 products to be processed in a single HTTP request? For batch imports, the answer is usually no. Better design separates submission from processing.

Optimization 1: Asynchronous Processing with Kafka

The API accepts the import request, publishes a task to Kafka, and returns a job identifier. Background consumers process records independently. Client receives:

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "jobId": "job-8f31a2",
  "status": "PROCESSING",
  "statusUrl": "/api/products/bulk/status/job-8f31a2"
}

Client polls the status endpoint:

GET /api/products/bulk/status/job-8f31a2

{
  "jobId": "job-8f31a2",
  "status": "PROCESSING",
  "total": 10000,
  "processed": 4500,
  "failed": 12
}

Benefits:

HTTP request returns immediately without waiting for all business operations

Processing scales independently of request handling

Failed records can be retried with explicit policies

Progress and error reporting become part of the job lifecycle

Kafka consumer groups distribute work across multiple instances

Kafka async processing architecture
Kafka async processing architecture

Optimization 2: Request Chunking

Not all workloads need Kafka. If the client can split the payload into smaller batches, a chunked API is simpler. Example:

POST /api/products/batch
Content-Type: application/json
X-Batch-Id: import-2026-001

[
  {"id": 1, "name": "Smartphone", "price": "2999.00", "stock": 150},
  {"id": 2, "name": "Laptop", "price": "5499.5", "stock": 80}
]

Client submits batches of 100 records (or other suitable size) instead of 10,000 at once. Advantages:

Smaller request bodies

Lower peak memory per request

Easier retry at batch level

More predictable request durations

Better control over concurrency and rate limiting

Trade-offs: additional HTTP requests and managing partial completion across batches. The batchId helps the server track progress and deduplicate retries. Clearly define: whether a batch is atomic, whether partial success is allowed, and how the client recovers from failed imports.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

batch processingKafkaProtobufSpring BootGZIP CompressionRequest OptimizationChunked RequestsJSON Streaming
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.