skip to content

What is @ProjectedPayload in Spring for GraphQL, and when would you use it instead of binding an @Argument to a POJO?

level: seniorimportance: nice to knowfreq 22%

answer

  1. @ProjectedPayload on an INTERFACE
  2. proxy over raw argument map (Spring Data projections)
  3. needs spring-data-commons on classpath
  4. getX -> key x; @Value SpEL to compute/rename
  5. read-only, nested projections; alternative to POJO binding

basics

~20 s

@ProjectedPayload marks an interface used to read GraphQL input arguments. Instead of binding to a concrete class, Spring creates a proxy over the raw argument map, and you access values through getter-style accessor methods on the interface.

solid answer

~50 s

@ProjectedPayload is an annotation on an *interface* that lets a controller method receive GraphQL input arguments as an interface-based projection rather than a concrete POJO. Spring (using Spring Data's projection support, so spring-data-commons must be on the classpath) creates a dynamic proxy backed by the raw arguments map. You declare accessor methods like `String getTitle()` and Spring maps them to argument keys; nested input types can be projected via nested @ProjectedPayload interfaces, and @Value with SpEL can compute or rename fields. It's handy when you want a read-only, flexible view over input without a dedicated class, or to expose only a subset of fields, or to project a subset of a large input. Compared to POJO binding — which instantiates and populates a concrete object via constructor/setters — projections avoid boilerplate DTOs and tolerate loosely-structured input.

code

java · 21 lines
java
// Interface projection over the input arguments (no concrete DTO)
@ProjectedPayload
public interface BookProjection {
    String getTitle();
    AuthorProjection getAuthor();          // nested projection

    @Value("#{target.title + ' (' + target.year + ')'}")
    String getDisplayLabel();              // computed via SpEL
}

@ProjectedPayload
interface AuthorProjection { String getName(); }

@Controller
class BookController {
    @MutationMapping
    public Book addBook(BookProjection book) {   // proxy over arg map
        return service.create(book.getTitle(), book.getAuthor().getName());
    }
}
// input BookInput { title: String!, year: Int, author: AuthorInput! }

go deeper

for a junior

Recognize it lets you read input arguments through an interface.

for a middle

Explain it's an interface proxy over the argument map and contrast with POJO binding.

for a senior

Detail Spring Data projection backing, nested projections, @Value SpEL, and the spring-data-commons requirement.

for a principal

Judge when read-only projections beat concrete DTOs, validation tradeoffs, and API-evolution/loose-coupling implications.

When a GraphQL field has arguments (especially an `input` type), Spring for GraphQL can bind them to a Java target. There are two styles: 1. **POJO binding** — `@Argument BookInput input` creates a concrete `BookInput` and populates it via constructor or setters through the `GraphQlArgumentBinder`. 2. **Interface projection** — `@ProjectedPayload` lets you receive an **interface** instead, backed by a proxy over the raw argument map. **How @ProjectedPayload works** - You define an interface annotated `@ProjectedPayload` with accessor (getter) methods: `String getTitle();`, `Integer getPages();`. - Spring uses **Spring Data's projection machinery** (`spring-data-commons` must be on the classpath) to create a dynamic proxy. The proxy resolves each accessor against the underlying arguments `Map` by property name (`getTitle` → key `title`). - Use it as a controller parameter; you may or may not add `@Argument`. Spring recognizes a parameter whose type is a `@ProjectedPayload` interface. **Extra capabilities** - **Nested projections**: an accessor can itself return another `@ProjectedPayload` interface for nested input objects. - **@Value + SpEL**: you can annotate an accessor with `@Value("#{target.firstName + ' ' + target.lastName}")` to compute or rename, mirroring Spring Data's open projections. - **Read-only**: projections are for *reading* input; they don't construct domain objects. **When to use vs POJO** - Use a **projection** when you want a lightweight, read-only, possibly partial view of input arguments without authoring a full DTO/record, or when the input is loosely structured and you only need a few fields. - Use **POJO/record binding** when you want a strongly-typed, validated (`@Valid`) domain/command object you'll pass deeper into the app, or when you need the concrete instance. **Gotchas** - Requires `spring-data-commons` on the classpath; without it the projection support isn't available. - Accessor names must match argument keys (JavaBean `getX` → `x`) or use `@Value` to remap. - Projections are proxies, not real objects — you can't pass them somewhere expecting a concrete type, and identity/equality behave like proxies. - Bean Validation on a projection interface isn't the same as validating a populated POJO; prefer POJO binding when you need `@Valid` constraint checking. **Summary**: `@ProjectedPayload` is a convenience for interface-based, read-only access to GraphQL input arguments, powered by Spring Data projections — a lighter alternative to declaring concrete input classes.

  • What dependency must be present for @ProjectedPayload to work?
    spring-data-commons, because the projection proxies reuse Spring Data's projection factory/machinery.
  • When would you prefer a POJO/record over a projection for input?
    When you want a strongly-typed, concrete command object to pass deeper, or when you need Bean Validation (@Valid) on a populated instance — projections are read-only proxies, not real objects.

saying these in an interview costs you the question

  • Saying @ProjectedPayload goes on a class rather than an interface
  • Claiming it constructs a real domain object (it's a read-only proxy)
  • Forgetting it depends on spring-data-commons
  • Thinking accessor names are arbitrary rather than mapped to argument keys

context