Open6

Mojolicious でGraphQLを触ってみた。

kfly8kfly8

Mojoliciousで、GraphQLの簡易ブログアプリを書いてみた。
お試し実装ではあるけれど、アプリケーションロジックの書きやすさやDataLoaderを使ったN+1対策なんかも書いてみている。リポジトリはこちら。ここでは、どんな実装にしたのか備忘のため書き留める。

https://github.com/kfly8/experimental-blog-app

kfly8kfly8

まず、GraphQLのスキーマはこちら。ブログのエントリーとそのエントリーに対するコメントが複数あるようなスキーマ。Query#entriesで、エントリーの一覧なんかが取得できると。

type Query {
    entries: [Entry!]
}

type Entry {
    id: ID!
    title: String!
    body: String!
    comments: [EntryComment!]
}

type EntryComment {
   id: ID!
   body: String!
}
kfly8kfly8

アーキテクチャはこんな具合。

% tree lib/Blog/GraphQL
lib/Blog/GraphQL
├── Query.pm ----- クエリのリゾルバ。QueryResolverの実装を集約する。
├── QueryResolver ---- クエリのリゾルバの各実装を格納庫
│  └── Entries.pm ----- entriesのリゾルバ
├── QueryResolverRole.pm ---- スキーマ情報を元に、リゾルバに対して制約やシュガーを提供
├── Schema.pm --- GraphQLのスキーマ
├── Type
│  ├── Entry.pm --- エントリーのtype object。各フィールドのリゾルバを書く。
│  └── EntryComment.pm --- エントリーコメントのtype object。
└── TypeObject.pm --- スキーマ情報を元に、type objectに対して制約やシュガーを提供
kfly8kfly8

実際にentries クエリの実装を書く場合は、リゾルバのQueryResolver::Entriesとタイプオブジェクトの2つを書くだけで良いように設計している。

まず、リゾルバのQueryResolver::Entriesは、次の通り、データをfetchする処理を書いている。
人気エントリーであったり、最近のエントリーだったり、ユーザに価値提供する肝になると思う。

package Blog::GraphQL::QueryResolver::Entries;
use v5.36;
use Moo;
with 'Blog::GraphQL::QueryResolverRole';

use Blog::Unit::Entry::EntryFetcher;

sub main($self, $args, $context, $info) {
    my $fetcher = Blog::Unit::Entry::EntryFetcher->entity;
    my $entries = $fetcher->select_all({});

    return $entries;
}

1;

次に、タイプオブジェクトは、次のような具合にフィールドごとにメソッドを定義すれば良い。
commentsだけは、N+1にならないようにDataLoaderを経由している。

package Blog::GraphQL::Type::Entry;
use v5.36;
use Moo;
extends qw(Blog::GraphQL::TypeObject);

use Blog::Unit::Entry::EntryCommentFetcher;

sub id($self, @) {
    $self->object->id;
}

sub title($self, @) {
    $self->object->title;
}

sub body($self, @) {
    $self->object->body;
}

sub comments($self, $args, $context, @) {
    my $loader = $self->data_loader($context, Blog::Unit::Entry::EntryCommentFetcher->can('batch_comments'));
    $loader->load($self->object->id);
}

1;
kfly8kfly8

このアーキテクチャの内部実装の肝は、スキーマ情報を元にした制約。
例えば、entriesクエリなら、Blog::GraphQL::QueryResolver::Entriesという命名でリゾルバするように制約を課している。

スキーマを元にしたコードの自動生成にはしていない。
Mojolicious x GraphQLのベストプラクティスがわかっていないから、吐き出すべきものがわかっていないのと、graphql-rubyの影響も受けて、コードを書く形にしているのもある。(アレはGraphQLスキーマも書いていて、こちらはスキーマはそうでないので、差分は多分にあるんだけど。)処理を挟んだり、柔軟にやる余地がある気がして、これはこれで良い気がしている。

kfly8kfly8

最後に。

おれおれでフレームワーク書くの楽しいですね。習作の目的は果たせたかなと。
graphql-perlは、自由度が高い一方、敷居が高いとも感じるので、
もしかしたらフレームワークのニーズがあるかも?どうなんだろ。
BlogといったアプリケーションやMojoliciousに依存せず、書いてみたいですね。