GraphQL を Spring Boot で試してみる 3

GraphQL Advent Calendar 2018 の18日目です。

最近、GraphQL を試しに触り始めてみたという程度です。普段は Java や Spring を使っています。というわけで、今回は Spring Boot で試してみたときの備忘録シリーズの第3弾となります。第1弾と第2弾はこちら。Query と Mutation を試してみたときの備忘録です。が、本当に触ってみた程度の内容です...。

今回は Subscription を試してみます。相変わらず触ってみた程度の内容ですが、サンプルコードはこちら。

kntmr/playground/graphql-spring-examples - GitHub


以下、備忘録。

依存ライブラリ

Subscription の Resolver からは Publisher<T> を返す。取り急ぎ、Spring WebFlux を追加。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

schema / データクラス

スキーマに Subscription の IF を定義。

// ... (略)
type Subscription {
    added(id: ID!): ToDo
}

Resolver

Subscription の場合は、GraphQLSubscriptionResolver インタフェースを実装する。今回は EmitterProcessor<T>Publisher<T> を返すことに。また、引数で指定された ID で filter することで、Subscribe したユーザーごとに Subscription を返すようにする。

@Component
public class TodoSubscriptionResolver implements GraphQLSubscriptionResolver {
    private TodoEmitterProcessor processor;
    public TodoSubscriptionResolver(TodoEmitterProcessor processor) {
        this.processor = processor;
    }
    public Publisher<ToDo> added(int id) {
        return processor.getProcessor().filter(todo -> todo.getUserId() == id);
    }
}

EmitterProcessor<T> を持つクラス。

@Component
public class TodoEmitterProcessor {
    private EmitterProcessor<ToDo> processor;
    public TodoEmitterProcessor() {
        processor = EmitterProcessor.create();
    }
    public EmitterProcessor<ToDo> getProcessor() {
        return processor;
    }
    public void sendMessage(ToDo todo) {
        processor.onNext(todo);
    }
}

Mutation で更新するときに EmitterProcessor#onNext する。

// ... (略)
ToDo newTodo = new ToDo( ... );
todoDao.add(newTodo);
processor.sendMessage(newTodo); // 追加

リクエスト / Subscription

アプリを起動するとコンソールに以下のログが出力されている。

Registering ServerEndpointConfig: ServerEndpointRegistration for path '/subscriptions': class graphql.servlet.GraphQLWebsocketServlet

というわけで、今回は GraphiQL ではなく、WebSocket で /subscriptions にリクエストする。

const ws = new WebSocket('ws://localhost:8080/subscriptions')
ws.onopen = (e) => {
  console.log('ws opened.')
  ws.send(JSON.stringify({
    query: `subscription {
      added(id: 1) { // ★
        id,
        content,
        completed
      }
    }`
  }));
}
ws.onmessage = (e) => {
  console.log('ws message.')
  var resp = JSON.parse(e.data);
  // ... (略)
}
ws.onclose = (e) => {
  console.log('ws closed.')
  ws.close();
}
ws.onerror = (e) => {
  console.log('error!!')
  ws.close();
}

GraphiQL などから Mutation でデータを更新すると、ws.onmessage でイベントが通知される。Resolver 側で ID で filter しているので、他ユーザーのデータを Mutation に投げた場合はイベントは通知されない。

所感

まだまだあやしいところはありますが、ここまでで Query / Mutation / Subscription の3機能をざっくり試してみました。今後はもう少し実践的な内容を調べていきたいと思います。特に、自動生成などの開発ツール周りを調べたい。

とりあえず、以下のドキュメントをもっと読み込んでみようかと。

GraphQL Java

その他

先日の JJUG CCC 2018 Fall で GraphQL のセッションがありました。

www.slideshare.net

デモのリポジトリはこちら。

github.com