编辑此页面

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 并进行适当的配置

架构

在本指南中,我们将构建一个简单的 GraphQL 应用程序,该应用程序将在 /graphql 处公开 GraphQL API。

此示例的灵感来自一个流行的 GraphQL API。

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载 存档

解决方案位于 microprofile-graphql-quickstart 目录 中。

创建 Maven 项目

首先,我们需要一个新项目。使用以下命令创建一个新项目

CLI
quarkus create app org.acme:microprofile-graphql-quickstart \
    --extension='quarkus-smallrye-graphql' \
    --no-code
cd microprofile-graphql-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=microprofile-graphql-quickstart \
    -Dextensions='quarkus-smallrye-graphql' \
    -DnoCode
cd microprofile-graphql-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果使用 Powershell,请将 -D 参数包装在双引号中,例如 "-DprojectArtifactId=microprofile-graphql-quickstart"

此命令将生成一个项目,并导入 smallrye-graphql 扩展。

如果您已经配置了 Quarkus 项目,可以通过在项目根目录中运行以下命令将 smallrye-graphql 扩展添加到您的项目中

CLI
quarkus extension add quarkus-smallrye-graphql
Maven
./mvnw quarkus:add-extension -Dextensions='quarkus-smallrye-graphql'
Gradle
./gradlew addExtension --extensions='quarkus-smallrye-graphql'

这会将以下内容添加到您的构建文件中

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
build.gradle
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 应用程序

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./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,并在 devtest 模式下默认启用它,但也可以通过将 quarkus.smallrye-graphql.ui.always-include 配置属性设置为 true 来显式配置生产模式。

可以从 https://:8080/q/graphql-ui/ 访问 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 响应是客户端决定的,因此客户端可以选择它需要的字段。

假设我们的客户端只需要 titlereleaseDate,这将导致对 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 查询方法请求的字段可以根据我们先前的示例来确定。这样我们就可以检索单个电影信息。

但是,假设我们的客户端需要 filmId01 的两部电影。在 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 来检索电影数据。现在我们希望允许客户端检索 FilmHero 数据。

将以下内容添加到我们的 FilmResource 类中

    public List<Hero> heroes(@Source Film film) { (1)
        return service.getHeroesByFilm(film);
    }
1 使 List<Hero> 数据能够添加到响应 Film 的查询中

通过添加此方法,我们有效地更改了 GraphQL API 的 schema。尽管 schema 已更改,但之前的查询仍然有效。因为我们只扩展了 API 来检索 FilmHero 数据。

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 很简单,只有两个具体类型:HeroFilm。现在我们想用其他类型来扩展我们的 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 字段

现在,更新我们的 HeroAlly 实体以实现此接口。

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 只允许我们直接查询实体或实体列表。现在我们希望允许客户端搜索我们的所有实体。虽然 HeroAlly 具有共享的抽象类型 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 的 namesurname。这是因为我们在 mutation 查询的 { } 中选择在响应中检索这些字段。这可以轻松成为客户端可能需要的服务器端生成的字段。

现在让我们尝试删除一个条目

mutation DeleteHero {
  deleteHero(id :3){
    name
    surname
  }
}

createHero mutation 方法类似,我们也检索了我们删除的 hero 的 namesurname,这在 { } 中定义。

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 将广播任何新 HeroMulti 处理器
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 中的 TypeInput 的复杂对象映射到现有标量。

将现有标量适配到另一个类型

public class Movie {

    @AdaptToScalar(Scalar.Int.class)
    Long idLongThatShouldChangeToInt;

    // ....
}

以上将 Long Java 类型适配到 Int Scalar 类型,而不是 默认BigInteger

将复杂对象适配到标量类型

public class Person {

    @AdaptToScalar(Scalar.String.class)
    Phone phone;

    // ....
}

这不会创建 GraphQL 中的 TypeInput,而是映射到 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 依赖项以解决此问题

pom.xml
<dependency>
    <groupId>org.eclipse.microprofile.metrics</groupId>
    <artifactId>microprofile-metrics-api</artifactId>
</dependency>
build.gradle
implementation("org.eclipse.microprofile.metrics:microprofile-metrics-api")

结论

SmallRye GraphQL 使客户端能够检索所需的确切数据,从而防止 过度获取获取不足

GraphQL API 可以扩展而不会破坏先前的查询,从而实现简单的 API 演进

配置参考

构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖

配置属性

类型

默认

将提供查询的根路径。默认为 graphql。默认情况下,此值将解析为相对于 ${quarkus.http.root-path} 的路径。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_ROOT_PATH

显示更多

字符串

graphql

启用 Apollo Federation。如果未指定此值,则如果应用程序中检测到任何 GraphQL Federation 注解,则会自动启用 federation。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_FEDERATION_ENABLED

显示更多

布尔值

为 federation 启用批量解析。默认禁用。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_FEDERATION_BATCH_RESOLVING_ENABLED

显示更多

布尔值

启用 metrics。默认情况下,此值为 false。如果设置为 true,则需要 metrics 扩展。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_METRICS_ENABLED

显示更多

布尔值

启用 tracing。默认情况下,如果添加了 tracing 扩展,则会启用它。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_TRACING_ENABLED

显示更多

布尔值

启用事件。允许您在引导和执行时接收事件。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_EVENTS_ENABLED

显示更多

布尔值

false

启用非阻塞支持。默认值为 true。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_NONBLOCKING_ENABLED

显示更多

布尔值

启用 GET 请求。允许通过 HTTP GET 进行查询。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_HTTP_GET_ENABLED

显示更多

布尔值

启用 POST 请求上的查询参数。允许 POST 请求覆盖或在查询参数中提供值。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_HTTP_POST_QUERYPARAMETERS_ENABLED

显示更多

布尔值

更改类型命名策略。所有可能的策略是:default, merge-inner-class, full

环境变量:QUARKUS_SMALLRYE_GRAPHQL_AUTO_NAME_STRATEGY

显示更多

字符串

默认

应包含在错误响应中的扩展字段列表。默认情况下,不包含任何字段。有效值的示例包括 [exception,classification,code,description,validationErrorType,queryPath]

环境变量:QUARKUS_SMALLRYE_GRAPHQL_ERROR_EXTENSION_FIELDS

显示更多

字符串列表

用于隐藏异常消息的默认错误消息。默认为 "Server Error"

环境变量:QUARKUS_SMALLRYE_GRAPHQL_DEFAULT_ERROR_MESSAGE

显示更多

字符串

将数据获取器异常打印到日志文件。在 dev 和 test 模式下默认为 true,在 prod 模式下默认为 false

环境变量:QUARKUS_SMALLRYE_GRAPHQL_PRINT_DATA_FETCHER_EXCEPTION

显示更多

布尔值

通过 HTTP 使 schema 可用。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_SCHEMA_AVAILABLE

显示更多

布尔值

true

在 schema 中包含 Scalar 定义。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_SCHEMA_INCLUDE_SCALARS

显示更多

布尔值

false

在 schema 中包含 schema 内部定义。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_SCHEMA_INCLUDE_SCHEMA_DEFINITION

显示更多

布尔值

false

在 schema 中包含指令。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_SCHEMA_INCLUDE_DIRECTIVES

显示更多

布尔值

false

在 schema 中包含内省类型。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_SCHEMA_INCLUDE_INTROSPECTION_TYPES

显示更多

布尔值

false

将 payload(以及可选的变量)记录到 System out。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_LOG_PAYLOAD

显示更多

off, query-only, query-and-variables

off

应解开的异常(类名)。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_UNWRAP_EXCEPTIONS

显示更多

字符串列表

服务器应支持的 graphql-over-websocket 用例的子协议。允许的子协议是 "graphql-ws" 和 "graphql-transport-ws"。默认情况下,两者都启用。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_WEBSOCKET_SUBPROTOCOLS

显示更多

字符串列表

如果应将忽略的字符捕获为 AST 节点,则设置为 true。默认为 false

环境变量:QUARKUS_SMALLRYE_GRAPHQL_PARSER_CAPTURE_IGNORED_CHARS

显示更多

布尔值

如果 `graphql.language.Comments` 应被捕获为 AST 节点,则设置为 true

环境变量:QUARKUS_SMALLRYE_GRAPHQL_PARSER_CAPTURE_LINE_COMMENTS

显示更多

布尔值

如果 `graphql.language.SourceLocations` 应被捕获为 AST 节点,则设置为 true。默认为 true

环境变量:QUARKUS_SMALLRYE_GRAPHQL_PARSER_CAPTURE_SOURCE_LOCATION

显示更多

布尔值

解析器将接受的原始 token 的最大数量,之后将抛出异常。默认为 15000

环境变量:QUARKUS_SMALLRYE_GRAPHQL_PARSER_MAX_TOKENS

显示更多

整数

解析器将接受的原始空格 token 的最大数量,之后将抛出异常。默认为 200000

环境变量:QUARKUS_SMALLRYE_GRAPHQL_PARSER_MAX_WHITESPACE_TOKENS

显示更多

整数

如果查询的总数据字段数量超过定义的限制,则中止查询。默认无限制

环境变量:QUARKUS_SMALLRYE_GRAPHQL_INSTRUMENTATION_QUERY_COMPLEXITY

显示更多

整数

如果查询的总深度超过定义的限制,则中止查询。默认无限制

环境变量:QUARKUS_SMALLRYE_GRAPHQL_INSTRUMENTATION_QUERY_DEPTH

显示更多

整数

要在 schema 中注册的其他标量。这些来自 graphql-java-extended-scalars 库。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_EXTRA_SCALARS

显示更多

list of uuid, object, json

是否启用 GraphQL UI。默认情况下,如果包含 GraphQL UI(参见 always-include),则启用 GraphQL UI。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_UI_ENABLE

显示更多

布尔值

true

指定 GraphQL schema 的字段可见性。此配置项允许您定义逗号分隔的模式列表(GraphQLType.GraphQLField)。这些模式用于确定应从 schema 中排除哪些字段。特殊值 no-introspection 将禁用内省字段。有关更多信息,请参阅 graphql-java 文档

环境变量:QUARKUS_SMALLRYE_GRAPHQL_FIELD_VISIBILITY

显示更多

字符串

default

排除 GraphQL 响应 data 字段中的所有 'null' 字段,但非成功解析的字段(错误)除外。默认禁用。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_EXCLUDE_NULL_FIELDS_IN_RESPONSES

显示更多

布尔值

应显示错误消息的运行时异常类名列表。默认情况下,运行时异常消息将被隐藏,并返回通用的 Server Error 消息。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_SHOW_RUNTIME_EXCEPTION_MESSAGE

显示更多

字符串列表

应隐藏错误消息的已检查异常类名列表。默认情况下,已检查异常消息将显示异常消息。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_HIDE_CHECKED_EXCEPTION_MESSAGE

显示更多

字符串列表

SmallRye GraphQL UI 配置

类型

默认

GraphQL UI 可用的路径。不允许值为 /,因为它会阻止应用程序提供任何其他内容。默认情况下,此 URL 将解析为相对于 ${quarkus.http.non-application-root-path} 的路径。

环境变量:QUARKUS_SMALLRYE_GRAPHQL_UI_ROOT_PATH

显示更多

字符串

graphql-ui

始终包含 UI。默认情况下,这仅包含在开发和测试中。将此设置为 true 还将在生产环境中包含 UI

环境变量:QUARKUS_SMALLRYE_GRAPHQL_UI_ALWAYS_INCLUDE

显示更多

布尔值

false

相关内容

关于相同主题