graphql-codegenで Typescriptの型を生成(Vue、Nuxt)

概要

Vueでgraphqlからクエリなどをしたときに、レスポンスに対してTSの型を定義する。

この型を自前で定義するのは億劫なので、 graphql-codegenというツールを使ってschemaなどから型を生成する。

一応、公式ドキュメントのVueの例を参考にしてNuxt(CSRのみ)で動作した。

graphqlのクライアントはapollo。

使い方

基本、公式ドキュメントの通りにやればできる。

インストール

For Yarn:

yarn add graphql
yarn add -D typescript ts-node @graphql-codegen/cli

Getting Stertedだとts-nodeが含まれていないが一応いれておく。

プロジェクトのルートに、 codegen.ts を作る。

今回の自分の場合だと、各参照先が公式通りにできなかったので少し修正した。

  • schema: ローカルのschema定義ファイルへのパス(graphqlのURLも可)
  • documents: ルート直下の全ディレクトリのvueファイルが対象
    • GraphQL documentsを含むパスを指定
    • 実際にリクエストを行うquerymutationsubscription, fragment が記載されているファイルが対象
    • composablesなファイルにも記載している場合は、 .vueだけだと検知できないので .ts も含める必要あり
  • generates: { ‘./gql/’: {省略
    • 生成物の出力先を指定する
import type { CodegenConfig } from '@graphql-codegen/cli'
 
const config: CodegenConfig = {
  schema: 'hoge/fuga/schema.graphql',
  documents: ['**/*.vue'],
  ignoreNoDocuments: true, // for better experience with the watcher
  generates: {
    './gql/': {
      preset: 'client',
      config: {
        useTypeImports: true
      }
    }
  }
}
 
export default config

リクエストのgraphqlクエリを準備する

公式の例がシンプルでわかりやいので、これをもとに補足する。

allFilmsWithVariablesQuery のクエリをgraphql-codegenが解析して型を生成してくれる。

個人的には、allFilmsWithVariablesQueryという命名よりも‘Query’なしで allFilmsWithVariables としたほうが生成された型の命名が良い感じになるので推奨したい。

生成された型は、 {クエリ名}Query で生成される。suffixにQueryが入るので、自分のコードからは「Query」を入れない。含めるとQueryQueryになる。(オプションで制御できそうな気もするが)

App.vue

<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import { computed } from 'vue'

import FilmItem from './components/FilmItem.vue'
import { graphql } from '../src/gql'

const { result } = useQuery(
  graphql(/* GraphQL */ `
    query allFilmsWithVariablesQuery($first: Int!) {
      allFilms(first: $first) {
        edges {
          node {
            ...FilmItem
          }
        }
      }
    }
  `),
  // variables are typed!
  { first: 10 }
)
// `films` is typed!
const films = computed(() => result.value?.allFilms?.edges?.map(e => e?.node!))
</script>

<template>
  <ul>
    <li v-for="film of films"><FilmItem :film="film" /></li>
  </ul>
</template>

型を生成

コマンドを実行する。codegen.tsで指定したパスに諸々の型定義が出力される。

yarn graphql-codegen

—watchをつけると実装しながら型を生成してくれる。

yarn graphql-codegen --watch

生成した型を参照する

生成されたファイルからインポートして型に使うだけ。

useQuery<{ここに指定}>

import type { allFilmsWithVariablesQuery } from "@/gql/graphql”
const { result } = useQuery<allFilmsWithVariablesQuery>(

AppSyncの場合

下記のようにschemaを拡張して、AppSyncのScalar typesに対応する必要があった。(色々とやり方はありそうだった)

const config: CodegenConfig = {
  schema: [
    "hoge/fuga/schema.graphql",
    "gql/aws.gql",
  ],
# <https://github.com/dotansimha/graphql-code-generator/discussions/4311#discussioncomment-2921796>
scalar AWSTime
scalar AWSDateTime
scalar AWSTimestamp
scalar AWSEmail
scalar AWSJSON
scalar AWSURL
scalar AWSPhone
scalar AWSIPAddress
scalar BigInt
scalar Double

directive @aws_subscribe(mutations: [String!]!) on FIELD_DEFINITION

# Allows transformer libraries to deprecate directive arguments.
directive @deprecated(reason: String!) on INPUT_FIELD_DEFINITION | ENUM

directive @aws_auth(cognito_groups: [String!]!) on FIELD_DEFINITION
directive @aws_api_key on FIELD_DEFINITION | OBJECT
directive @aws_iam on FIELD_DEFINITION | OBJECT
directive @aws_oidc on FIELD_DEFINITION | OBJECT
directive @aws_cognito_user_pools(
  cognito_groups: [String!]
) on FIELD_DEFINITION | OBJECT

おまけ、その他

  • ウィザード
    • 今回はcodegen.tsをマニュアルで作ったが、ウィザードがあるっぽいので試してみると便利かも
  • schemaの参照先をgraphqlのエンドポイントで試してみたい(一応、認可のためのヘッダーもカスタム可能っぽい)
  • リゾルバーやFragmentsあたりでも使えそうなので試したい
  • AWSのamplifyでも同じようなものがあるのでAppSyncで実装しているのであれば、これのほうが親和性あるかも
無料相談実施中
AWSを使用したサーバーレスアプリケーションの構築
サーバーレス開発内製化、AWSへの移行等
様々な課題について無料でご相談お受けいたします。
最新情報をチェックしよう!