JSUG勉強会 2021年その2 Spring GraphQLをとことん語る夕べでの発表のスライドとコード例です。 発表時はSpring GraphQL 1.0.0-M1でしたがM2、M3とバージョンを重ねているためたまにスライドを更新しています。 JSUGでの発表当時のスライドは20210806-jsugタグを参照してください。
PlantUMLで描いた図をビルドする。
java -jar ~/plantuml.jar -tsvg docs/plantuml.pu
スライドをビルドする。
npx @marp-team/marp-cli@latest --html --output docs/index.html docs/slide.md
/docs
をGitHub Pagesでホスティングするように設定しているので次のURLでスライドが見られる。
./mvnw spring-boot:run
ブラウザで http://localhost:8080/graphiql を開く。
スライドにもあったクエリーを試す。
query {
article(id: 1) {
id
title
content
category {
id
name
}
}
}
タイトルだけ取得するようにしてみる。
query {
article(id: 1) {
title
}
}
変数を使ってみる。
query GetArticle($id: ID!) {
article(id: $id) {
title
}
}
{
"id": 1
}
curl
でも試してみる。
curl -s http://localhost:8080/graphql -H "Content-Type: application/json" -d '{"query": "{article(id: 1) { id, title, content, category { id, name } }}"}' | jq
subscription
操作も試してみる。
subscription {
count
}
結果のエリアにカウントアップされて1から10まで表示される。
wscat
でも確認してみる。
wscat
はnpm install -g wscat
でインストールできる。
wscat --connect ws://localhost:8080/graphql
subscription
のプロトコルはまだ理解していないので、Spring GraphQLのコードを読んでわかった手順を実施する。
まずはconnection_init
が必要。
{"type": "connection_init"}
それからsubscribe
。
待っていると1秒おきにカウントアップする値が返される。
{"type": "subscribe", "id": "...", "payload": {"query": "subscription { count }"}}
もちろん変数も使える。
{"type": "subscribe", "id": "...", "payload": {"query": "subscription Count($size: Int!) { count(size: $size) }", "variables": {"size": 5 }}}
まずはN + 1。
query {
comics {
title
publisher {
name
}
}
}
Fetch Query.comics
以降のログを見るとcomics
で1回、comics.publisher
で10回のクエリーが発行されていることがわかる。
次にDataLoader版。
query {
comics {
title
author {
name
}
}
}
Fetch Query.comics
以降のログを見るとcomics
とcomics.author
が共に1回ずつのクエリー発行で済んでいることがわかる。
まずはafter
を指定せずクエリーーを発行して返ってくる値を確認する。
query GitCommits {
history {
forward(first: 3) {
edges {
node {
hash
message
}
cursor
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
}
それからpageInfo
の値を見ながらafter
を設定しつつクエリーを試す。
query GitCommits {
history {
forward(first: 3, after: "3") {
edges {
node {
hash
message
}
cursor
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
}
後方も試す。
query GitCommits {
history {
backward(last: 3, before: "7") {
edges {
node {
hash
message
}
cursor
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
}
次のクエリーを実行するとエラー(Unauthorized
)になる。
{
security {
protected
}
}
REQUEST HEADERSという場所に次のJSONを書いて実行するとエラーにならず値が返ってくる。
{
"Authorization": "Basic ZGVtbzpzZWNyZXQ="
}
これは該当のDataFetcher
内で呼び出されているコンポーネントのメソッドに@PreAuthorize("isAuthenticated()")
を付けている。
カスタムdirective
で認証を表現した例も作ってみた。
次のクエリーが@authenticated
というカスタムdirective
で保護したフィールドへのアクセスとなる。
{
security {
protected2
}
}
Authorization
ヘッダーの有無による違いを試してみてほしい。
curl -s localhost:8080/actuator/metrics/graphql.request | jq
curl -s localhost:8080/actuator/metrics/graphql.datafetcher | jq
curl -s localhost:8080/actuator/metrics/graphql.error | jq
graphqlvizを使うとGraphQLスキーマを図にできる。
npx graphqlviz http://localhost:8080/graphql | dot -Tpng -o graphql-schema.png
次のような図が生成される。
スライド(docs/
配下にあるファイル)はCC BY 4.0、ソースコード(スライド以外のファイル)はMITを適用します。