Java Serverless on AWS Lambda · Frameworks, Native Images and SDK Clients

API Gateway Events and Response Shapes in Java

Why a Java API returns 500 after switching from a REST API to an HTTP API, and how to handle event formats and binary bodies.

Scenario

Example app: a Java handler uses APIGatewayProxyRequestEvent (REST API, payload 1.0). The team switches the front door to an HTTP API to save cost and adds an endpoint that accepts image uploads.

What you see in production

After the switch, handlers see null HTTP methods and paths; the upload endpoint stores corrupted images that look like long base64 strings.

The naive code

public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context ctx) {
    String method = event.getHttpMethod();             // null for HTTP API payload 2.0
    byte[] image = event.getBody().getBytes(UTF_8);    // body is base64 when isBase64Encoded is true
    store(image);
    return new APIGatewayProxyResponseEvent().withStatusCode(201);
}

Reproduce it

Base64 bodies must be decoded
import java.nio.charset.StandardCharsets;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        byte[] png = {(byte) 0x89, 'P', 'N', 'G', 13, 10, 26, 10};               // PNG signature
        String body = Base64.getEncoder().encodeToString(png);                      // how API Gateway delivers binary
        boolean isBase64Encoded = true;

        byte[] naive = body.getBytes(StandardCharsets.UTF_8);
        byte[] fixed = isBase64Encoded ? Base64.getDecoder().decode(body) : body.getBytes(StandardCharsets.UTF_8);
        System.out.println("naive bytes: " + naive.length + " " + Arrays.toString(Arrays.copyOf(naive, 4)));
        System.out.println("fixed bytes: " + fixed.length + " " + Arrays.toString(Arrays.copyOf(fixed, 4)) + " (valid PNG header: " + (fixed[1] == 'P') + ")");
    }
}

Output

naive bytes: 12 [105, 86, 66, 79]
fixed bytes: 8 [-119, 80, 78, 71] (valid PNG header: true)

Run this on your computer — see “Get Started” for setup.

Root cause

REST APIs send payload format 1.0 (APIGatewayProxyRequestEvent); HTTP APIs default to payload format 2.0 (APIGatewayV2HTTPEvent, method in requestContext.http.method, rawPath). Binary bodies arrive base64-encoded with isBase64Encoded = true, and responses with binary content must be base64-encoded too.

The fix

  • Match the handler's input type to the integration: APIGatewayV2HTTPEvent / APIGatewayV2HTTPResponse for HTTP APIs and function URLs, or configure the HTTP API integration for payload 1.0 if you must keep the old types.
  • Decode bodies when isBase64Encoded is true; for responses, set isBase64Encoded(true) and encode binary content.
  • Better for uploads: issue presigned S3 URLs so files never pass through Lambda (payload limits and cost).
public APIGatewayV2HTTPResponse handleRequest(APIGatewayV2HTTPEvent event, Context ctx) {
    String method = event.getRequestContext().getHttp().getMethod();
    byte[] body = event.getIsBase64Encoded()
        ? Base64.getDecoder().decode(event.getBody())
        : event.getBody().getBytes(StandardCharsets.UTF_8);
    // ...
    return APIGatewayV2HTTPResponse.builder().withStatusCode(201).build();
}

Trade-offs

  • Supporting both payload formats in one handler adds code; pick one per function.
  • Proxying binary through Lambda is limited by payload size and costs duration; presigned URLs scale better.
  • Frameworks (Serverless Java Container, Spring Cloud Function) hide the formats but add start-up cost.

Detect it in production

  • Null fields after infrastructure changes; contract tests with sample events of each format.
  • Stored files whose size is about 4/3 of the original (undecoded base64).
  • 413 errors for large uploads.

Key points

  • Payload 1.0 (REST API) and 2.0 (HTTP API, function URLs) need different event types.
  • Decode base64 bodies; encode binary responses.
  • Use presigned S3 URLs for uploads.

Further reading: HTTP API Lambda integrations and payload formats · aws-lambda-java-events on GitHub

Practice questions

Which Java event type should a handler behind an HTTP API use?

Answer

APIGatewayV2HTTPEvent with APIGatewayV2HTTPResponse, matching payload format 2.0 (also used by function URLs). REST APIs use APIGatewayProxyRequestEvent and APIGatewayProxyResponseEvent (payload 1.0).

Why might an uploaded image be stored corrupted by a Java Lambda function?

Answer

API Gateway delivers binary bodies base64-encoded with isBase64Encoded set to true; writing the body string's bytes stores the base64 text instead of the decoded binary.

What is a better way to handle large file uploads than sending them through Lambda?

Answer

Return a presigned S3 PUT URL so the client uploads directly to S3; process the file afterwards from an S3 event, avoiding payload limits and paying for upload time in Lambda.

Open in the app to mark lessons complete, take notes and track your progress.