You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/asciidoc/mvc-api.adoc
+53Lines changed: 53 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -577,6 +577,59 @@ If a request is made to `/bar?foo=baz`, the result will be `foo is: baz` because
577
577
578
578
==== Responses
579
579
580
+
===== Projections
581
+
582
+
The MVC module provides first-class support for Projections via annotations. This allows you to define the response view declaratively, keeping your controller logic clean and focused on data retrieval.
583
+
584
+
====== Usage
585
+
586
+
There are two ways to define a projection in an MVC controller.
587
+
588
+
You can annotate your method with `@Project` and provide the selection DSL:
589
+
590
+
.Via @Project Annotation
591
+
[source,java]
592
+
----
593
+
@GET
594
+
@Project("(id, name)")
595
+
public List<User> listUsers() {
596
+
return service.findUsers();
597
+
}
598
+
----
599
+
600
+
Alternatively, you can define the projection directly within the HTTP method annotation (e.g., `@GET`, `@POST`) using the `projection` attribute:
The Jooby Annotation Processor automatically handles the conversion of your method's return type. You are **not forced** to return a `Projected` instance; you can simply return your POJO or Collection, and Jooby will wrap it for you at compile-time.
614
+
615
+
However, if you need manual control (for example, to dynamically toggle validation), you can still return a `Projected` instance explicitly:
616
+
617
+
[source,java]
618
+
----
619
+
@GET
620
+
public Projected<User> getUser(String id) {
621
+
User user = service.findById(id);
622
+
return Projected.wrap(user)
623
+
.failOnMissingProperty(true)
624
+
.include("(id, status)");
625
+
}
626
+
----
627
+
628
+
[NOTE]
629
+
====
630
+
For more details on the Selection DSL syntax and available JSON engines, please refer to the <<core-responses-projections, Core Projections documentation>>.
631
+
====
632
+
580
633
===== Status Code
581
634
582
635
The default HTTP status code returned by an MVC route is `200 OK`, except for `void` methods annotated with `@DELETE`, which automatically return `204 No Content`.
Copy file name to clipboardExpand all lines: docs/asciidoc/responses.adoc
+66Lines changed: 66 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -37,6 +37,72 @@ Raw responses are **not** processed by a <<core-context-response-body-message-en
37
37
38
38
Even if a JSON encoder is installed, a raw response is always sent directly to the client bypassing the encoder.
39
39
40
+
==== Projections
41
+
42
+
Projections allow API consumers to request a partial representation of a resource. This feature is a more flexible, dynamic, and powerful alternative to the standard Jackson `@JsonView` annotation.
43
+
44
+
While inspired by the "selection set" philosophy of **GraphQL**, it is important to note that this is **not** a GraphQL implementation. Instead, it provides a "Light GraphQL" experience for RESTful endpoints, allowing you to define exactly which fields should be serialized in the JSON response without the overhead of a full GraphQL engine.
45
+
46
+
===== Basic Usage
47
+
48
+
To enable a projection, you wrap your response object using the `Projected.wrap` utility. The projection syntax is parsed and applied to the underlying object or collection.
49
+
50
+
[source,java]
51
+
----
52
+
get("/users/{id}", ctx -> {
53
+
User user = repository.findById(ctx.path("id").value());
54
+
55
+
return Projected.wrap(user)
56
+
.include("(id, name, email)");
57
+
});
58
+
----
59
+
60
+
===== Comparison with @JsonView
61
+
62
+
If you have used Jackson's `@JsonView`, you will find Projections far more capable:
63
+
64
+
* **Dynamic**: Unlike `@JsonView`, which requires static class markers defined at compile-time, Projections are defined at runtime.
65
+
* **Ad-hoc**: You can create any combination of fields on the fly without adding new Java interfaces or classes.
66
+
* **Deep Nesting**: Projections easily handle deeply nested object graphs, whereas `@JsonView` can become difficult to manage with complex relationships.
67
+
68
+
===== Projection DSL
69
+
70
+
The `include` method accepts a string using a simple, nested syntax:
71
+
72
+
* **Field Selection**: `(id, name)` returns only those two fields.
73
+
* **Nested Selection**: `(id, address(city, country))` selects the `id` and specific fields from the nested `address` object.
74
+
* **Wildcards**: `(id, address(*))` selects the `id` and all available fields within the `address` object.
75
+
* **Deep Nesting**: `(id, orders(id, items(name, price)))` allows for recursion into the object graph.
76
+
77
+
===== Validation
78
+
79
+
By default, the projection engine **does not validate** that requested fields exist on the target class (`failOnMissingProperty` is `false`). This allows for maximum flexibility, especially when working with polymorphic types or dynamic data where certain fields may only exist on specific subclasses.
80
+
81
+
If you prefer strict enforcement to prevent API consumers from requesting non-existent fields, you can enable validation:
82
+
83
+
[source,java]
84
+
----
85
+
return Projected.wrap(data)
86
+
.failOnMissingProperty(true)
87
+
.include("(id, name, strictFieldOnly)");
88
+
----
89
+
90
+
===== More Information
91
+
92
+
Support for Projections extends beyond core scripting to include high-level annotations and documentation generation.
93
+
94
+
* **MVC Support**: Projections can be applied to controller methods using the `@Project` annotation. See the <<web-mvc-api-responses-projections, MVC documentation>> for details.
95
+
* **OpenAPI Support**: Jooby automatically generates pruned schemas for your Swagger documentation. See the link:modules/openapi[OpenAPI documentation] for details.
96
+
97
+
[NOTE]
98
+
====
99
+
**Implementation Note:**
100
+
The `Projection` core API defines the structure and the DSL. The actual runtime filtering is performed by your chosen JSON module:
0 commit comments