SmallRye GraphQL
本指南演示了您的 Quarkus 应用程序如何使用 SmallRye GraphQL,这是 MicroProfile GraphQL 规范的实现。
正如 GraphQL 规范网站所述
GraphQL 是一种用于 API 的查询语言,也是用于通过现有数据满足这些查询的运行时。GraphQL 提供了对 API 中数据的完整且易于理解的描述,使客户端能够仅请求他们需要的内容,而无需更多,从而随着时间的推移更容易地演进 API,并支持强大的开发工具。
GraphQL 最初由 Facebook 于 2012 年开发,自 2015 年起成为开放标准。
GraphQL 并非 REST API 规范的替代品,而仅仅是一个选择。与 REST 不同,GraphQL API 能够通过以下方式使客户端受益:
- 防止过度获取和获取不足
-
REST API 是服务器驱动的固定数据响应,客户端无法确定。尽管客户端不需要所有字段,但客户端必须检索所有数据,从而导致
过度获取
。根据第一次调用(HATEOAS),客户端可能还需要多个 REST API 调用才能检索所有所需数据,从而导致获取不足
。 - API 演进
-
由于 GraphQL API 返回客户端请求的数据,因此为现有 API 添加其他字段和功能不会对现有客户端造成破坏性更改。
先决条件
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
解决方案
我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。
克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或下载 存档。
解决方案位于 microprofile-graphql-quickstart
目录 中。
创建 Maven 项目
首先,我们需要一个新项目。使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数包装在双引号中,例如"-DprojectArtifactId=microprofile-graphql-quickstart"
此命令将生成一个项目,并导入 smallrye-graphql
扩展。
如果您已经配置了 Quarkus 项目,可以通过在项目根目录中运行以下命令将 smallrye-graphql
扩展添加到您的项目中
quarkus extension add quarkus-smallrye-graphql
./mvnw quarkus:add-extension -Dextensions='quarkus-smallrye-graphql'
./gradlew addExtension --extensions='quarkus-smallrye-graphql'
这会将以下内容添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-graphql")
准备应用程序:GraphQL API
在本节中,我们将开始创建 GraphQL API。
首先,创建以下实体,代表来自遥远星系的电影
package org.acme.microprofile.graphql;
public class Film {
public String title;
public Integer episodeID;
public String director;
public LocalDate releaseDate;
}
public class Hero {
public String name;
public String surname;
public Double height;
public Integer mass;
public Boolean darkSide;
public LightSaber lightSaber;
public List<Integer> episodeIds = new ArrayList<>();
}
enum LightSaber {
RED, BLUE, GREEN
}
为了提高可读性,我们使用了具有公共字段的类,但具有私有字段和公共 getter 和 setter 的类也可以正常工作。 |
我们刚刚创建的类描述了 GraphQL schema,它是一组客户端可以访问的可能数据(对象、字段、关系)。
让我们继续看一个示例 CDI bean,它将作为存储库工作
@ApplicationScoped
public class GalaxyService {
private List<Hero> heroes = new ArrayList<>();
private List<Film> films = new ArrayList<>();
public GalaxyService() {
Film aNewHope = new Film();
aNewHope.title = "A New Hope";
aNewHope.releaseDate = LocalDate.of(1977, Month.MAY, 25);
aNewHope.episodeID = 4;
aNewHope.director = "George Lucas";
Film theEmpireStrikesBack = new Film();
theEmpireStrikesBack.title = "The Empire Strikes Back";
theEmpireStrikesBack.releaseDate = LocalDate.of(1980, Month.MAY, 21);
theEmpireStrikesBack.episodeID = 5;
theEmpireStrikesBack.director = "George Lucas";
Film returnOfTheJedi = new Film();
returnOfTheJedi.title = "Return Of The Jedi";
returnOfTheJedi.releaseDate = LocalDate.of(1983, Month.MAY, 25);
returnOfTheJedi.episodeID = 6;
returnOfTheJedi.director = "George Lucas";
films.add(aNewHope);
films.add(theEmpireStrikesBack);
films.add(returnOfTheJedi);
Hero luke = new Hero();
luke.name = "Luke";
luke.surname = "Skywalker";
luke.height = 1.7;
luke.mass = 73;
luke.lightSaber = LightSaber.GREEN;
luke.darkSide = false;
luke.episodeIds.addAll(Arrays.asList(4, 5, 6));
Hero leia = new Hero();
leia.name = "Leia";
leia.surname = "Organa";
leia.height = 1.5;
leia.mass = 51;
leia.darkSide = false;
leia.episodeIds.addAll(Arrays.asList(4, 5, 6));
Hero vader = new Hero();
vader.name = "Darth";
vader.surname = "Vader";
vader.height = 1.9;
vader.mass = 89;
vader.darkSide = true;
vader.lightSaber = LightSaber.RED;
vader.episodeIds.addAll(Arrays.asList(4, 5, 6));
heroes.add(luke);
heroes.add(leia);
heroes.add(vader);
}
public List<Film> getAllFilms() {
return films;
}
public Film getFilm(int id) {
return films.get(id);
}
public List<Hero> getHeroesByFilm(Film film) {
return heroes.stream()
.filter(hero -> hero.episodeIds.contains(film.episodeID))
.collect(Collectors.toList());
}
public void addHero(Hero hero) {
heroes.add(hero);
}
public Hero deleteHero(int id) {
return heroes.remove(id);
}
public List<Hero> getHeroesBySurname(String surname) {
return heroes.stream()
.filter(hero -> hero.surname.equals(surname))
.collect(Collectors.toList());
}
}
现在,让我们创建我们的第一个 GraphQL API。
将 org.acme.microprofile.graphql.FilmResource
类编辑如下
@GraphQLApi (1)
public class FilmResource {
@Inject
GalaxyService service;
@Query("allFilms") (2)
@Description("Get all Films from a galaxy far far away") (3)
public List<Film> getAllFilms() {
return service.getAllFilms();
}
}
1 | @GraphQLApi 注解表示 CDI bean 将成为 GraphQL 端点 |
2 | @Query 注解定义了此方法将可使用 allFilms 名称进行查询 |
3 | 可查询方法的文档 |
@Query 注解的值是可选的,如果省略,则默认为方法名。 |
这样我们就创建了第一个可查询的 API,稍后我们将对其进行扩展。
启动
以开发模式启动 quarkus 应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
内省
可以通过调用以下内容检索 GraphQL API 的完整 schema
curl https://:8080/graphql/schema.graphql
服务器将返回 GraphQL API 的完整 schema。
GraphQL UI
实验性 - 未包含在 MicroProfile 规范中 |
GraphQL UI 是一个允许轻松与 GraphQL API 交互的强大工具。
Quarkus smallrye-graphql
扩展附带 GraphiQL,并在 dev
和 test
模式下默认启用它,但也可以通过将 quarkus.smallrye-graphql.ui.always-include
配置属性设置为 true
来显式配置生产模式。
可以从 https://:8080/q/graphql-ui/ 访问 GraphQL UI。
有关如何为 GraphQL UI 添加/删除安全性的信息,请参阅 Web 端点授权 指南。
查询 GraphQL API
现在访问已在 dev
模式下部署的 GraphQL UI 页面。
在 GraphQL UI 中输入以下查询并按 play
按钮
query allFilms {
allFilms {
title
director
releaseDate
episodeID
}
}
由于我们的查询包含 Film
类中的所有字段,因此我们将在响应中检索所有字段。由于 GraphQL API 响应是客户端决定的,因此客户端可以选择它需要的字段。
假设我们的客户端只需要 title
和 releaseDate
,这将导致对 API 的先前调用 过度获取
了不必要的数据。
在 GraphQL UI 中输入以下查询并点击 play
按钮
query allFilms {
allFilms {
title
releaseDate
}
}
请注意,响应中我们只检索了所需的字段。因此,我们防止了 过度获取
。
让我们通过向 FilmResource
类添加以下内容来继续扩展我们的 GraphQL API。
@Query
@Description("Get a Films from a galaxy far far away")
public Film getFilm(@Name("filmId") int id) {
return service.getFilm(id);
}
请注意,我们已省略了 @Query 注解中的值。因此,查询的名称隐含地设置为方法名,省略了 get 。 |
此查询将允许客户端通过 ID 检索电影,并且参数上的 @Name
注解会将参数名称更改为 filmId
,而不是如果省略 @Name
注解的默认值 id
。
在 GraphQL UI
中输入以下内容并发出请求。
query getFilm {
film(filmId: 1) {
title
director
releaseDate
episodeID
}
}
film
查询方法请求的字段可以根据我们先前的示例来确定。这样我们就可以检索单个电影信息。
但是,假设我们的客户端需要 filmId
为 0
和 1
的两部电影。在 REST API 中,客户端必须向 API 发出两次调用。因此,客户端将 获取不足
。
在 GraphQL 中,可以一次发出多个查询。
在 GraphQL UI
中输入以下内容以检索两部电影
query getFilms {
film0: film(filmId: 0) {
title
director
releaseDate
episodeID
}
film1: film(filmId: 1) {
title
director
releaseDate
episodeID
}
}
这使客户端能够在一个请求中获取所需的数据。
扩展 API
到目前为止,我们已经创建了一个 GraphQL API 来检索电影数据。现在我们希望允许客户端检索 Film
的 Hero
数据。
将以下内容添加到我们的 FilmResource
类中
public List<Hero> heroes(@Source Film film) { (1)
return service.getHeroesByFilm(film);
}
1 | 使 List<Hero> 数据能够添加到响应 Film 的查询中 |
通过添加此方法,我们有效地更改了 GraphQL API 的 schema。尽管 schema 已更改,但之前的查询仍然有效。因为我们只扩展了 API 来检索 Film
的 Hero
数据。
在 GraphQL UI
中输入以下内容以检索电影和英雄数据。
query getFilmHeroes {
film(filmId: 1) {
title
director
releaseDate
episodeID
heroes {
name
height
mass
darkSide
lightSaber
}
}
}
响应现在包含电影的英雄。
批量处理
当您公开像我们 getAllFilms
这样的 Collection
返回时,您可能希望使用上述的批量形式,以更有效地获取英雄
public List<List<Hero>> heroes(@Source List<Film> films) { (1)
// Here fetch all hero lists
}
1 | 这里接收批量电影,允许您获取相应的英雄。 |
非阻塞
可以通过使用 Uni
作为返回类型,或在方法上添加 @NonBlocking
来使查询具有响应性
@Query
@Description("Get a Films from a galaxy far far away")
public Uni<Film> getFilm(int filmId) {
// ...
}
或者您可以使用 @NonBlocking
@Query
@Description("Get a Films from a galaxy far far away")
@NonBlocking
public Film getFilm(int filmId) {
// ...
}
使用 Uni
或 @NonBlocking
意味着请求将在 Event-loop 线程而不是 Worker 线程上执行。
您可以在一个请求中混合阻塞和非阻塞,
@Query
@Description("Get a Films from a galaxy far far away")
@NonBlocking
public Film getFilm(int filmId) {
// ...
}
public List<Hero> heroes(@Source Film film) {
return service.getHeroesByFilm(film);
}
以上将在 event-loop 线程上获取电影,但会切换到 worker 线程来获取英雄。
抽象类型
当前的 schema 很简单,只有两个具体类型:Hero
和 Film
。现在我们想用其他类型来扩展我们的 API,并添加一些抽象,使客户端与之交互变得容易。
接口
让我们给我们的英雄一些盟友。
首先,创建一个新实体来表示我们的 Ally
。
public class Ally {
public String name;
public String surname;
public Hero partner;
}
更新 GalaxyService
以拥有一些盟友。
private List<Ally> allies = new ArrayList();
public GalaxyService() {
// ...
Ally jarjar = new Ally();
jarjar.name = "Jar Jar";
jarjar.surname = "Binks";
allies.add(jarjar);
}
public List<Ally> getAllAllies() {
return allies;
}
我们还更新 FilmResource
以允许客户端查询所有盟友
@Query
public List<Ally> allies() {
return service.getAllAllies();
}
在 GraphQL UI
中输入以下内容并发出请求。
query getAllies {
allies {
name
surname
}
}
请注意,Ally
具有与 Hero
相同的字段。为了使客户端查询更轻松,让我们创建一个所有角色的抽象。
创建一个新的 Java 接口来定义我们常见的角色特征。
public interface Character {
(1)
String getName();
String getSurname();
}
1 | 接口中定义的 Getter 将定义它包含的 GraphQL 字段 |
现在,更新我们的 Hero
和 Ally
实体以实现此接口。
public class Hero implements Character {
// ...
(1)
public String getName() {
return name;
}
(1)
public String getSurname() {
return surname;
}
}
public class Ally implements Character {
// ...
(1)
public String getName() {
return name;
}
(1)
public String getSurname() {
return surname;
}
}
1 | 由于接口不能定义字段,我们必须实现 getter |
通过添加接口并更新现有实体以实现它,我们有效地更改了 schema。更新后的 schema 将包含新的 Ally
类型和 Character
接口。
(1)
interface Character {
name: String
surname: String
}
(2)
type Ally implements Character {
name: String
surname: String
partner: Hero
}
(3)
type Hero implements Character {
name: String
surname: String
# ...
}
1 | Character 接口使用 getter 定义为字段 |
2 | 添加了 Ally 类型,它实现了 Character |
3 | 更新了 Hero 类型以实现 Character |
更新我们的 GalaxyService
以提供所有角色。
public List<Character> getAllCharacters() {
List<Character> characters = new ArrayList<>();
characters.addAll(heroes);
characters.addAll(allies);
return characters;
}
现在我们可以允许客户端查询所有角色,而不仅仅是英雄。
将以下内容添加到我们的 FilmResource
类中
@Query
@Description("Get all characters from a galaxy far far away")
public List<Character> characters() {
return service.getAllCharacters();
}
在 GraphQL UI
中输入以下内容并发出请求。
query getCharacters {
characters {
name
surname
}
}
联合类型
实验性 - 未包含在 MicroProfile 规范中 |
到目前为止,我们的 API 只允许我们直接查询实体或实体列表。现在我们希望允许客户端搜索我们的所有实体。虽然 Hero
和 Ally
具有共享的抽象类型 Character
,但没有一个抽象包含 Film
。
首先,创建这个新的抽象类型,代表搜索结果的可能返回类型。
package org.acme.microprofile.graphql;
import io.smallrye.graphql.api.Union;
@Union (1)
public interface SearchResult {
}
1 | 需要 @Union 来指示此 Java 接口代表 GraphQL 联合类型,而不是 GraphQL 接口 |
表示 GraphQL 联合类型的 Java 接口不必为空,但定义的任何 getter 都不会显式更改 GraphQL schema。 |
更新我们的实体以实现 SearchResult
public class Film implements SearchResult {
// ...
}
public interface Character implements SearchResult {
// ...
}
public class Hero implements Character {
// ...
}
public class Ally implements Character {
// ...
}
更新 GalaxyService
以提供搜索
public List<SearchResult> search(String query) {
List<SearchResult> results = new ArrayList<>();
List<Film> matchingFilms = films.stream()
.filter(film -> film.title.contains(query)
|| film.director.contains(query))
.collect(Collectors.toList());
results.addAll(matchingFilms);
List<Character> matchingCharacters = getAllCharacters().stream()
.filter(character -> character.getName().contains(query)
|| character.getSurname().contains(query))
.collect(Collectors.toList());
results.addAll(matchingCharacters);
return results;
}
将以下内容添加到我们的 FilmResource
类中
@Query
@Description("Search for heroes or films")
public List<SearchResult> search(String query) {
return service.search(query);
}
在 GraphQL UI
中输入以下内容并发出请求。
query searchTheGalaxy {
search(query: "a") {
... on Film {
title
director
}
... on Character {
name
surname
}
}
}
由于 SearchResult 联合包含实现它的成员,因此我们可以使用 Character 接口。 |
Mutations(变更)
Mutations 用于创建、更新或删除数据。
现在让我们为 GraphQL API 添加添加和删除英雄的功能。
将以下内容添加到我们的 FilmResource
类中
@Mutation
public Hero createHero(Hero hero) {
service.addHero(hero);
return hero;
}
@Mutation
public Hero deleteHero(int id) {
return service.deleteHero(id);
}
在 GraphQL UI
中输入以下内容以插入 Hero
mutation addHero {
createHero(hero: {
name: "Han",
surname: "Solo"
height: 1.85
mass: 80
darkSide: false
episodeIds: [4, 5, 6]
}
)
{
name
surname
}
}
通过使用此 mutation,我们在服务中创建了一个 Hero
实体。
请注意,在响应中我们检索了创建的 Hero 的 name
和 surname
。这是因为我们在 mutation 查询的 { }
中选择在响应中检索这些字段。这可以轻松成为客户端可能需要的服务器端生成的字段。
现在让我们尝试删除一个条目
mutation DeleteHero {
deleteHero(id :3){
name
surname
}
}
与 createHero
mutation 方法类似,我们也检索了我们删除的 hero 的 name
和 surname
,这在 { }
中定义。
Subscriptions(订阅)
Subscriptions 允许您订阅查询。它允许您接收事件,并使用 WebSockets。有关更多详细信息,请参阅 GraphQL over WebSocket Protocol 规范。
示例:我们想知道何时创建新的 Heroes
BroadcastProcessor<Hero> processor = BroadcastProcessor.create(); (1)
@Mutation
public Hero createHero(Hero hero) {
service.addHero(hero);
processor.onNext(hero); (2)
return hero;
}
@Subscription
public Multi<Hero> heroCreated(){
return processor; (3)
}
1 | 将广播任何新 Hero 的 Multi 处理器 |
2 | 添加新 Hero 时,也对其进行广播 |
3 | 在运行时使流在 schema 和 WebSocket 上可用 |
任何现在连接到 /graphql
WebSocket 连接的客户端都将在创建新 Heroes 时收到事件
subscription ListenForNewHeroes {
heroCreated {
name
surname
}
}
按字段创建查询
也可以按单个字段进行查询。例如,让我们创建一个方法来按姓氏查询英雄。
将以下内容添加到我们的 FilmResource
类中
@Query
public List<Hero> getHeroesWithSurname(@DefaultValue("Skywalker") String surname) {
return service.getHeroesBySurname(surname);
}
通过使用 @DefaultValue
注解,我们确定当未提供参数时,surname 的值为 Skywalker
。
使用 GraphQL UI
测试以下查询
query heroWithDefaultSurname {
heroesWithSurname{
name
surname
lightSaber
}
}
query heroWithSurnames {
heroesWithSurname(surname: "Vader") {
name
surname
lightSaber
}
}
Context(上下文)
您可以使用此实验性的、SmallRye 特有的功能在代码中的任何位置获取有关 GraphQL 请求的信息。
@Inject
Context context;
或者作为您的方法参数,如果您在 GraphQLApi
类中,例如
@Query
@Description("Get a Films from a galaxy far far away")
public Film getFilm(Context context, int filmId) {
// ...
}
Context 对象允许您获取
-
原始请求(查询/变更)
-
参数
-
路径
-
选定的字段
-
任何变量
这允许您优化到数据存储的下游查询。
有关更多详细信息,请参阅 JavaDoc。
GraphQL-Java
此 Context 对象还允许您通过使用泄露的抽象来使用底层 graphql-java 功能。
DataFetchingEnvironment dfe = context.unwrap(DataFetchingEnvironment.class);
您也可以在 schema 生成期间访问底层的 graphql-java
,以直接添加自己的功能。
public GraphQLSchema.Builder addMyOwnEnum(@Observes GraphQLSchema.Builder builder) {
// Here add your own features directly, example adding an Enum
GraphQLEnumType myOwnEnum = GraphQLEnumType.newEnum()
.name("SomeEnum")
.description("Adding some enum type")
.value("value1")
.value("value2").build();
return builder.additionalType(myOwnEnum);
}
通过使用 @Observer
,您可以将任何内容添加到 Schema 构建器。
要使 Observer 工作,您需要启用事件。在 application.properties 中,添加以下内容:quarkus.smallrye-graphql.events.enabled=true 。 |
适配
适配到标量类型
另一个 SmallRye 特有的实验性功能,允许您将现有标量(已由实现映射到特定 Java 类型)映射到另一个类型,或者将通常会创建 GraphQL 中的 Type
或 Input
的复杂对象映射到现有标量。
将现有标量适配到另一个类型
public class Movie {
@AdaptToScalar(Scalar.Int.class)
Long idLongThatShouldChangeToInt;
// ....
}
以上将 Long
Java 类型适配到 Int
Scalar 类型,而不是 默认 的 BigInteger
。
将复杂对象适配到标量类型
public class Person {
@AdaptToScalar(Scalar.String.class)
Phone phone;
// ....
}
这不会创建 GraphQL 中的 Type
或 Input
,而是映射到 String 标量。
要执行上述操作,Phone
对象需要有一个接受 String(或 Int
/ Date
/ 等)的构造函数,或者有一个接受 String(或 Int
/ Date
/ 等)的 setter 方法,或者有一个 fromString
(或 fromInt
/ fromDate
- 取决于 Scalar 类型)静态方法。
例如
public class Phone {
private String number;
// Getters and setters....
public static Phone fromString(String number) {
Phone phone = new Phone();
phone.setNumber(number);
return phone;
}
}
有关 @AdaptToScalar
功能的更多信息,请参阅 JavaDoc。
使用 Adapter 适配
对于更复杂的情况,另一个选项是提供一个 Adapter。然后您可以在 Adapter 中自己进行映射。
有关 AdaptWith
功能的更多信息,请参阅 JavaDoc。
例如
public class Profile {
// Map this to an email address
@AdaptWith(AddressAdapter.class)
public Address address;
// other getters/setters...
}
public class AddressAdapter implements Adapter<EmailAddress, Address> {
@Override
public Address from(EmailAddress email) {
Address a = new Address();
a.addressType = AddressType.email;
a.addLine(email.getValue());
return a;
}
@Override
public EmailAddress to(Address address) {
if (address != null && address.addressType != null && address.addressType.equals(AddressType.email)) {
return new EmailAddress(address.lines.get(0));
}
return null;
}
}
也支持 @JsonbTypeAdapter 。 |
内置 Map 支持
默认情况下,由于 Map 在 schema 中难以建模(因为键和值可以在运行时动态确定),GraphQL 默认不支持 Map。使用上述适配,为 Quarkus 添加了 Map
支持,并将其映射到具有可选键参数的 Entry<Key,Value>
。这允许您返回一个 Map,并可选地按键查询它。
示例
@Query
public Map<ISO6391, Language> language() {
return languageService.getLanguages();
}
public enum ISO6391 {
af,
en,
de,
fr
}
public class Language {
private ISO6391 iso6391;
private String nativeName;
private String enName;
private String please;
private String thankyou;
// Getters & Setters
}
键和值对象可以是 Enum、Scalar 或复杂对象 |
您现在可以查询整个 Map 以及所有字段
{
language{
key
value {
enName
iso6391
nativeName
please
thankyou
}
}
}
这将返回类似这样的结果
{
"data": {
"language": [
{
"key": "fr",
"value": {
"enName": "french",
"iso6391": "fr",
"nativeName": "français",
"please": "s'il te plaît",
"thankyou": "merci"
}
},
{
"key": "af",
"value": {
"enName": "afrikaans",
"iso6391": "af",
"nativeName": "afrikaans",
"please": "asseblief",
"thankyou": "dankie"
}
},
{
"key": "de",
"value": {
"enName": "german",
"iso6391": "de",
"nativeName": "deutsch",
"please": "bitte",
"thankyou": "danke dir"
}
},
{
"key": "en",
"value": {
"enName": "english",
"iso6391": "en",
"nativeName": "english",
"please": "please",
"thankyou": "thank you"
}
}
]
}
}
您也可以按键查询
{
language (key:af){
value {
please
thankyou
}
}
}
这将只返回 Map 中的该值
{
"data": {
"language": [
{
"value": {
"please": "asseblief",
"thankyou": "dankie"
}
}
]
}
}
默认 Map Adapter 可以被我们自己的实现覆盖。 |
错误代码
您可以使用(SmallRye 特有的)@ErrorCode
为 GraphQL 响应中的错误输出添加错误代码。
@ErrorCode("some-business-error-code")
public class SomeBusinessException extends RuntimeException {
// ...
}
当发生 SomeBusinessException
时,错误输出将包含错误代码。
{
"errors": [
{
"message": "Unexpected failure in the system. Jarvis is working to fix it.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"annotatedCustomBusinessException"
],
"extensions": {
"exception": "io.smallrye.graphql.test.apps.error.api.ErrorApi$AnnotatedCustomBusinessException",
"classification": "DataFetchingException",
"code": "some-business-error-code" (1)
}
}
],
"data": {
...
}
}
1 | 错误代码 |
其他说明
如果您正在使用 smallrye-graphql
扩展,并且存在 micrometer
metrics 扩展且启用了 metrics,您可能会遇到 java.lang.NoClassDefFoundError
,因为 smallrye-graphql
扩展的某些版本对 Microprofile Metrics API 有运行时要求。添加以下 MicroProfile Metrics API 依赖项以解决此问题
<dependency>
<groupId>org.eclipse.microprofile.metrics</groupId>
<artifactId>microprofile-metrics-api</artifactId>
</dependency>
implementation("org.eclipse.microprofile.metrics:microprofile-metrics-api")
配置参考
构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖
配置属性 |
类型 |
默认 |
---|---|---|
将提供查询的根路径。默认为 graphql。默认情况下,此值将解析为相对于 环境变量: 显示更多 |
字符串 |
|
启用 Apollo Federation。如果未指定此值,则如果应用程序中检测到任何 GraphQL Federation 注解,则会自动启用 federation。 环境变量: 显示更多 |
布尔值 |
|
为 federation 启用批量解析。默认禁用。 环境变量: 显示更多 |
布尔值 |
|
启用 metrics。默认情况下,此值为 false。如果设置为 true,则需要 metrics 扩展。 环境变量: 显示更多 |
布尔值 |
|
启用 tracing。默认情况下,如果添加了 tracing 扩展,则会启用它。 环境变量: 显示更多 |
布尔值 |
|
启用事件。允许您在引导和执行时接收事件。 环境变量: 显示更多 |
布尔值 |
|
启用非阻塞支持。默认值为 true。 环境变量: 显示更多 |
布尔值 |
|
启用 GET 请求。允许通过 HTTP GET 进行查询。 环境变量: 显示更多 |
布尔值 |
|
启用 POST 请求上的查询参数。允许 POST 请求覆盖或在查询参数中提供值。 环境变量: 显示更多 |
布尔值 |
|
更改类型命名策略。所有可能的策略是:default, merge-inner-class, full 环境变量: 显示更多 |
字符串 |
|
应包含在错误响应中的扩展字段列表。默认情况下,不包含任何字段。有效值的示例包括 [exception,classification,code,description,validationErrorType,queryPath] 环境变量: 显示更多 |
字符串列表 |
|
用于隐藏异常消息的默认错误消息。默认为 "Server Error" 环境变量: 显示更多 |
字符串 |
|
将数据获取器异常打印到日志文件。在 dev 和 test 模式下默认为 环境变量: 显示更多 |
布尔值 |
|
通过 HTTP 使 schema 可用。 环境变量: 显示更多 |
布尔值 |
|
在 schema 中包含 Scalar 定义。 环境变量: 显示更多 |
布尔值 |
|
在 schema 中包含 schema 内部定义。 环境变量: 显示更多 |
布尔值 |
|
在 schema 中包含指令。 环境变量: 显示更多 |
布尔值 |
|
在 schema 中包含内省类型。 环境变量: 显示更多 |
布尔值 |
|
将 payload(以及可选的变量)记录到 System out。 环境变量: 显示更多 |
|
|
应解开的异常(类名)。 环境变量: 显示更多 |
字符串列表 |
|
服务器应支持的 graphql-over-websocket 用例的子协议。允许的子协议是 "graphql-ws" 和 "graphql-transport-ws"。默认情况下,两者都启用。 环境变量: 显示更多 |
字符串列表 |
|
如果应将忽略的字符捕获为 AST 节点,则设置为 true。默认为 false 环境变量: 显示更多 |
布尔值 |
|
如果 `graphql.language.Comments` 应被捕获为 AST 节点,则设置为 true 环境变量: 显示更多 |
布尔值 |
|
如果 `graphql.language.SourceLocations` 应被捕获为 AST 节点,则设置为 true。默认为 true 环境变量: 显示更多 |
布尔值 |
|
解析器将接受的原始 token 的最大数量,之后将抛出异常。默认为 15000 环境变量: 显示更多 |
整数 |
|
解析器将接受的原始空格 token 的最大数量,之后将抛出异常。默认为 200000 环境变量: 显示更多 |
整数 |
|
如果查询的总数据字段数量超过定义的限制,则中止查询。默认无限制 环境变量: 显示更多 |
整数 |
|
如果查询的总深度超过定义的限制,则中止查询。默认无限制 环境变量: 显示更多 |
整数 |
|
要在 schema 中注册的其他标量。这些来自 环境变量: 显示更多 |
list of |
|
是否启用 GraphQL UI。默认情况下,如果包含 GraphQL UI(参见 环境变量: 显示更多 |
布尔值 |
|
指定 GraphQL schema 的字段可见性。此配置项允许您定义逗号分隔的模式列表(GraphQLType.GraphQLField)。这些模式用于确定应从 schema 中排除哪些字段。特殊值 环境变量: 显示更多 |
字符串 |
|
排除 GraphQL 响应 环境变量: 显示更多 |
布尔值 |
|
应显示错误消息的运行时异常类名列表。默认情况下,运行时异常消息将被隐藏,并返回通用的 环境变量: 显示更多 |
字符串列表 |
|
应隐藏错误消息的已检查异常类名列表。默认情况下,已检查异常消息将显示异常消息。 环境变量: 显示更多 |
字符串列表 |
|
类型 |
默认 |
|
GraphQL UI 可用的路径。不允许值为 环境变量: 显示更多 |
字符串 |
|
始终包含 UI。默认情况下,这仅包含在开发和测试中。将此设置为 true 还将在生产环境中包含 UI 环境变量: 显示更多 |
布尔值 |
|