|
1 | 1 | = Spring for GraphQL Documentation
|
2 |
| -Brian Clozel; Andreas Marek; Rossen Stoyanchev |
| 2 | +Brian Clozel; Andreas Marek; Rossen Stoyanchev; Mark Paluch |
3 | 3 | include::attributes.adoc[]
|
4 | 4 |
|
5 | 5 |
|
@@ -480,6 +480,58 @@ assertThat(books.get(0).getName()).isEqualTo("...");
|
480 | 480 | [[data]]
|
481 | 481 | == Data Integration
|
482 | 482 |
|
| 483 | +Spring for GraphQL is, in contrast to other GraphQL technologies that surface persistent |
| 484 | +data, not a data gateway that translates GraphQL queries into SQL or JSON queries. |
| 485 | +Instead, Spring for GraphQL is an API gateway that leverages existing Spring technology |
| 486 | +following common programming models to expose underlying data sources through GraphQL. |
| 487 | + |
| 488 | +Domain-driven design is the suggested approach to manage complexity when using Spring Data. |
| 489 | +So by design, a GraphQL API built on top of Spring Data can leverage only what's already |
| 490 | +provided by an application. |
| 491 | +It, therefore, must adhere to the constraints of an aggregate. |
| 492 | +By definition, an aggregate is only valid if it is loaded in its entirety. |
| 493 | +Partially loaded aggregates may impose a limitation on aggregate functionality. |
| 494 | + |
| 495 | +With Spring Data you can choose whether you want to let your aggregate participate as |
| 496 | +an underlying data model to be directly exposed as a GraphQL result, or whether you want to |
| 497 | +apply projections to your data model before returning it as a GraphQL operation result. |
| 498 | + |
| 499 | +The advantage of using aggregates is that you do not require additional code to expose |
| 500 | +data through repositories. When processing a GraphQL operation, the integration layer |
| 501 | +transforms the field selection set into property paths. It provides these hints of which |
| 502 | +properties to materialize to the underlying Spring Data module that limits the field |
| 503 | +(or column) selection. |
| 504 | + |
| 505 | +Sometimes, an already reduced set of fields can be useful when exposing data. Also, some |
| 506 | +arrangements might require transformations to be applied before data is returned for a |
| 507 | +GraphQL operation. Spring Data supports for these scenarios projections: Interface and DTO |
| 508 | +Projections. |
| 509 | + |
| 510 | +Interface projections define a fixed set of properties to expose. Properties may or may |
| 511 | +not be `null`, depending on the query result. A plain, https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces.closed[closed interface projection] |
| 512 | +can be useful if you cannot partially materialize the aggregate object but you still |
| 513 | +want to expose a subset of properties. |
| 514 | + |
| 515 | +You can use interface projections to apply a lightweight set of data transformations, |
| 516 | +such as concatenations, computations or applying a static function to a property. |
| 517 | + |
| 518 | +https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections.interfaces.open[Open projections] |
| 519 | +leverage Spring's `@Value` annotation and |
| 520 | +{spring-framework-ref-docs}/core.html#expressions[SpEL expressions]for transformations. |
| 521 | + |
| 522 | +In both cases, interface projections define which properties to load from the underlying |
| 523 | +data source. |
| 524 | + |
| 525 | +DTO projections offer the highest possible level of customization as you can place your |
| 526 | +transformation code either in the constructor or the getter method. |
| 527 | + |
| 528 | +DTO projections materialize from a query where the individual properties are |
| 529 | +determined by the projection itself. DTO projections are commonly used with full-args |
| 530 | +constructors (e.g. Java records) and therefore they can only be constructed if all |
| 531 | +required fields (or columns) are part of the database query result. |
| 532 | + |
| 533 | + |
| 534 | + |
483 | 535 | [[data-querydsl]]
|
484 | 536 | === Querydsl
|
485 | 537 |
|
@@ -607,6 +659,36 @@ building a `QuerydslDataFetcher` you will need to use builder methods to apply i
|
607 | 659 | https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections[interface and DTO projections]
|
608 | 660 | to transform query results before returning these for further GraphQL processing.
|
609 | 661 |
|
| 662 | +To use Spring Data projections with Querydsl repositories, create either a projection interface |
| 663 | +or a target DTO class and configure it through the `projectAs` method to obtain a |
| 664 | +`DataFetcher` producing the target type: |
| 665 | + |
| 666 | +[source,java,indent=0,subs="verbatim,quotes"] |
| 667 | +---- |
| 668 | + class Account { |
| 669 | +
|
| 670 | + String name, identifier, description; |
| 671 | +
|
| 672 | + Person owner; |
| 673 | + } |
| 674 | +
|
| 675 | + interface AccountProjection { |
| 676 | +
|
| 677 | + String getName(); |
| 678 | +
|
| 679 | + String getIdentifier(); |
| 680 | + } |
| 681 | +
|
| 682 | + // For single result queries |
| 683 | + DataFetcher<AccountProjection> dataFetcher = |
| 684 | + QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).single(); |
| 685 | +
|
| 686 | + // For multi-result queries |
| 687 | + DataFetcher<Iterable<AccountProjection>> dataFetcher = |
| 688 | + QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).many(); |
| 689 | +---- |
| 690 | + |
| 691 | + |
610 | 692 |
|
611 | 693 | [[data-querydsl-registration]]
|
612 | 694 | ==== Auto Registration
|
@@ -685,6 +767,36 @@ it is supported, so no extra setup is required to enable it.
|
685 | 767 | https://docs.spring.io/spring-data/commons/docs/current/reference/html/#projections[interface and DTO projections]
|
686 | 768 | to transform query results before returning these for further GraphQL processing.
|
687 | 769 |
|
| 770 | +To use Spring Data projections with Query by Example repositories, create either a projection interface |
| 771 | +or a target DTO class and configure it through the `projectAs` method to obtain a |
| 772 | +`DataFetcher` producing the target type: |
| 773 | + |
| 774 | +[source,java,indent=0,subs="verbatim,quotes"] |
| 775 | +---- |
| 776 | + class Account { |
| 777 | +
|
| 778 | + String name, identifier, description; |
| 779 | +
|
| 780 | + Person owner; |
| 781 | + } |
| 782 | +
|
| 783 | + interface AccountProjection { |
| 784 | +
|
| 785 | + String getName(); |
| 786 | +
|
| 787 | + String getIdentifier(); |
| 788 | + } |
| 789 | +
|
| 790 | + // For single result queries |
| 791 | + DataFetcher<AccountProjection> dataFetcher = |
| 792 | + QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).single(); |
| 793 | +
|
| 794 | + // For multi-result queries |
| 795 | + DataFetcher<Iterable<AccountProjection>> dataFetcher = |
| 796 | + QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).many(); |
| 797 | +---- |
| 798 | + |
| 799 | + |
688 | 800 |
|
689 | 801 | [[data-querybyexample-registration]]
|
690 | 802 | ==== Auto Registration
|
|
0 commit comments