好きこそものの上手なるby yukichi

プログラミング学習の備忘録。万歳車輪の再発明。

graqhQLのtutorialやってみたメモ📝

situation

最近NestJSとprisma, graphQLを使ったAPI構築を勉強していて、 いまいちprismaとgraphQLの繋がりがふわふぁっとしていたので、howtogrqphQLという公式のtutorialをやってみた。 やっぱり公式はわかりやすい。。。 リマインドのために、サイトの訳(By 翻訳サイト)と自分の所感の雑記

公式サイト

memo

TS関係

  • @ts-ignore コメントをすると、次の行の型チェックが無視される
  • index.jsindex.ts はnodeJSではデフォルトのエントリーポイントと見なされる、最初に読み込まれるよ
  • 実行コマンド:npx ts-node src/script.ts

graqhQLのschemaって?

  • GraphQL schemas はGraphQL Schema Definition Language (SDL)の中で定義される
  • それぞれのGraphQL schemaは3つのオリジナルのroot typeを持つよ: Query, Mutation, Subscription
  • GraphQLは5このデフォルトのscalar typesがあるよ: Int, Float, String, Boolean and ID

Nexusライブラリについて

  • type-safe GraphQL schemasをcode-first approachで生成するライブラリ
    • (´-`).。oO:graphQL schemaのtypeとかrootTypeをライブラリを使って書いて生成していくのがcode-first approachって言うのかしらね。逆はSDL firstって言うらしいから、これは直接SDLを書きこむんだろうなぁ。

Nexusを使って、graphQLのtypeやroot object typesを記述していく。 objectTypeはGraphQLの中で新しいtypeを作るために作られる。 ↓Userタイプの例

export const User = objectType({
    name: "User",
    definition(t) {
        t.nonNull.int("id");
        t.nonNull.string("name");
        t.nonNull.string("email");
    }
})

②コマンドnpx ts-node --transpile-only src/schemaでGraphQL SDL とtypesを生成する。-> typeとかQuery, Mutationは見た目的に完成

③追加されたフィールドに対応するresolverを実装する

resolverって?

  • resolverはGraphQLフィールドの実装です。各タイプのすべてのフィールド(ルートタイプを含む)は、そのフィールドに対応するデータを返す役割を持つresolverが用意されています。
    • (´-`).。oO: resolverでデータのCRUD処理を実際にするイメージ。 例えばLinkというTypeとfeedというQueryがあった場合
type Link {
  createdAt: DateTime!
  description: String!
  id: Int!
  postedBy: User
  url: String!
  voters: [User!]!
}
type Query {
  link(id: Int!): Link
}
export const LinkQuery = extendType({
  type: "Query",
  t.field("link", {
            type: "Link",
            args: {
                id: nonNull(intArg())
            },  // ここまでlinkという名前のqueryを定義
        // ここから実際にlinkQueryがどういう処理をするか定義(Prisma利用)
            resolve(parent, args, context, info){
                // console.log(args) //{ id: 1 }
                const { id } = args
                return context.prisma.findUnique({
                    where: {
                        id: id
                    }
                })
            }
        })
  },
  • GraphQLサーバーがすべきことは、クエリに含まれるフィールドに対してすべてのresolverを呼び出し、クエリの形状(この場合Link)に従ってレスポンスをパッケージ化することである。このように、クエリーの解決はresolverの呼び出しをオーケストレーションするプロセスに過ぎないのである。
  • (´-`).。oO: GraphQLのQueryは該当するresolverを呼び出して、うまくまとめて返してくれるのね
  • all GraphQL resolver functionsは常に4つの引き数を受け取る: parent, args, context, info
    • contextとは?: context 引数は JavaScript のプレーンなオブジェクトで、リゾルバーチェーン内のすべての リゾルバーが読み込みと書き込みを行うことができる。したがって、これは基本的にリゾルバが通信するための手段である
    • Prisma Clientをcontextにぶっこむと、resolver内からPrisma Clientにアクセスできます!
    • Prisma Clientには、データベースに対するクエリーを実行するために必要なものがすべて含まれています。
    • Prismaのクエリは、非同期なのでPromiseオブジェクトを返します。
    • parentのくだり

PrismaとgraphQLの関係

  • (´-`).。oO: GraphQLがこーゆー名前のqueryもしくはmutationの処理あるよ、それは何を返すよって、宣言して、実際そのデータのCRUD処理を行うのがresolverで、データベースを使うときにはprismaを使ってCRUD処理のやりとりをdbとしてるって感じ。
  • (´-`).。oO: 例えばこーゆタイプ(User)があるよ、その中でこういうリレーションがあるよって示しているのがSDLで、実際にそのリレーションのデータのやり取りをするのがresolveって感じ
  • objectType内でもrelationがあったらresolveする。↓
// in User.ts>User = objectType

t.nonNull.list.nonNull.field("links", {    // 1
            type: "Link",
            resolve(parent, args, context) {   // 2
                return context.prisma.user  // 3
                    .findUnique({ where: { id: parent.id } })
                    .links();
            },
        }); 
  • 上について公式曰く: リンクフィールドのリゾルバは自明ではないので、データベースから返されるUserオブジェクトが自動的にリンクタイプを含まないため、GraphQLは自動的にそれを推論することができません。このため、Userタイプの他のフィールドとは異なり、リンクのリゾルバを明示的に定義する必要があります。
  • 上のコードの.links()について:prisma.shcemaのUser modelに記述している
links     Link[]        @relation(name: "PostedBy")

のlinksのことを指しているっぽい

  • (´-`).。oO: prisma shemaに書いても、objectTypeに記述しないと、graphQLには反映されないんだなぁ
  • 多対多のrelationならそれぞれのobjectTypeにその旨記載

その他

  • idArg: string, intArg(): intで詰まる
  • [User!]!の意味:空のリストか、NULLでないUserオブジェクトのみを含むリストを受け取るの意
  • pagination: Prisma Client APIは、findManyクエリの追加オプションとしてskiptakeの引数を受け取り、それに応じてリンクレコードを返します。

参考できるもの

github.com