graqhQLのtutorialやってみたメモ📝
situation
最近NestJSとprisma, graphQLを使ったAPI構築を勉強していて、 いまいちprismaとgraphQLの繋がりがふわふぁっとしていたので、howtogrqphQLという公式のtutorialをやってみた。 やっぱり公式はわかりやすい。。。 リマインドのために、サイトの訳(By 翻訳サイト)と自分の所感の雑記
memo
TS関係
@ts-ignore
コメントをすると、次の行の型チェックが無視される- index.js や index.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で生成するライブラリ
①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クエリの追加オプションとしてskipとtakeの引数を受け取り、それに応じてリンクレコードを返します。