0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

コード譜投稿アプリを作る#5 (graphql-code-generatorで型定義する)

Last updated at Posted at 2022-09-19

←前回の記事

今回やること

前回はフロント側からgraphqlクエリを叩いてScoreの一覧を表示しましたが、Scoreの型はこちらで手動で作りました。
前回のコード↓

index.tsx
...

type Score = {
  id: Number
  title: String
}

...

これをgraphql-code-generatorでコマンド一発で型定義を作成できるようにします。

ライブラリのインストール

まずライブラリをインストールします。

$ yarn add -D @graphql-codegen/cli

他にも必要なプラグインをインストール

$ yarn add --dev @graphql-codegen/typescript @graphql-codegen/typescript-graphql-request @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo

セットアップ

以下のコマンドを実行すると設定ファイルを対話式で作ってくれます。(設定ファイルを自分で作ってもよいです)

$ npx graphql-codegen init

色々聞かれるので答えていきます。
私が回答した内容は以下です

? Where is your schema?: (path or url) http://localhost:3000/graphql
? Where are your operations and fragments?: ./graphql/documents/**/*.graphql
? Pick plugins: TypeScript (required by other typescript plugins), TypeScript Operations (operations and fragments), TypeScript React Apollo (typed 
components and HOCs)
? Where to write the output: ./graphql/generated.tsx
? Do you want to generate an introspection file? No
? How to name the config file? codegen.yml
? What script in package.json should run the codegen? codegen

一応大事そうなところを説明すると、

Where is your schema?

サーバーサイドのgraphqlのエンドポイントを書いてます。このエンドポイントからschemaを読み取って型定義などしてるっぽいです。

Where are your operations and fragments?

フロントで使うqueryやmutationなどを書くファイル(これから作ります)のパスを作ります。今回はgraphqlというフォルダを作って、queryは/graphql/query/〇〇.graphql、mutationは/graphql/mutation/〇〇.graphqlに書こうと思っているので、このようにしています。

Where to write the output?

コマンドを打った時にここに型定義が出力されます。

How to name the config file?
What script in package.json should run the codegen?

package.jsonに設定に応じたコマンドが追加されます。

できた設定ファイルがこちら

codegen.yml
overwrite: true
schema: "http://localhost:3000/graphql"
documents: "./graphql/documents/**/*.graphql"
generates:
  ./graphql/generated.tsx:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-react-apollo"

package.jsonにもコマンドが追加されています。

package.json
  ...
  "private": true,
  "scripts": {
    "dev": "next dev -p 3001",
    "build": "next build",
    "start": "next start",
+   "lint": "next lint",
+   "codegen": "graphql-codegen --config codegen.yml"
  },
  "dependencies": {
  ...

コマンド実行

以上で設定は完了しました!
コマンドを実行する前にフロントで使用するqueryを書いていきます。
前回使ったScoreの一覧を取得するqueryを書いていきます。

/graphql/documents/query/scores.graphql
query Scores {
  scores {
    id
    title
  }
}

これでコマンドを実行します。
yarn以下はpackage.jsonにさきほど追記されたコマンドですね

$ yarn codegen

無事、以下のように型定義が作成されました!

/graphql/generated.tsx
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
const defaultOptions = {} as const;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: string;
  String: string;
  Boolean: boolean;
  Int: number;
  Float: number;
  ISO8601DateTime: any;
};

export type Mutation = {
  __typename?: 'Mutation';
  /** An example field added by the generator */
  testField: Scalars['String'];
};

export type Query = {
  __typename?: 'Query';
  score: Score;
  scores: Array<Score>;
};


export type QueryScoreArgs = {
  id?: InputMaybe<Scalars['Int']>;
};

export type Score = {
  __typename?: 'Score';
  createdAt: Scalars['ISO8601DateTime'];
  id: Scalars['ID'];
  title?: Maybe<Scalars['String']>;
  updatedAt: Scalars['ISO8601DateTime'];
};

export type ScoresQueryVariables = Exact<{ [key: string]: never; }>;


export type ScoresQuery = { __typename?: 'Query', scores: Array<{ __typename?: 'Score', id: string, title?: string | null }> };


export const ScoresDocument = gql`
    query Scores {
  scores {
    id
    title
  }
}
    `;

/**
 * __useScoresQuery__
 *
 * To run a query within a React component, call `useScoresQuery` and pass it any options that fit your needs.
 * When your component renders, `useScoresQuery` returns an object from Apollo Client that contains loading, error, and data properties
 * you can use to render your UI.
 *
 * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
 *
 * @example
 * const { data, loading, error } = useScoresQuery({
 *   variables: {
 *   },
 * });
 */
export function useScoresQuery(baseOptions?: Apollo.QueryHookOptions<ScoresQuery, ScoresQueryVariables>) {
        const options = {...defaultOptions, ...baseOptions}
        return Apollo.useQuery<ScoresQuery, ScoresQueryVariables>(ScoresDocument, options);
      }
export function useScoresLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ScoresQuery, ScoresQueryVariables>) {
          const options = {...defaultOptions, ...baseOptions}
          return Apollo.useLazyQuery<ScoresQuery, ScoresQueryVariables>(ScoresDocument, options);
        }
export type ScoresQueryHookResult = ReturnType<typeof useScoresQuery>;
export type ScoresLazyQueryHookResult = ReturnType<typeof useScoresLazyQuery>;
export type ScoresQueryResult = Apollo.QueryResult<ScoresQuery, ScoresQueryVariables>;

作成された型定義を使用して書き換え

実際に生成された型定義を使って、前回作ったindex.tsxを書き換えてみたのが以下です。

/pages/index.tsx
import { Box } from '@chakra-ui/react'
import type { ReactElement } from 'react'
import { Layout } from '../components/common/Layout'
import type { NextPageWithLayout } from './_app'
import { useQuery } from "@apollo/client";
import { Score, ScoresDocument } from '../graphql/generated';

const Page: NextPageWithLayout = () => {
  const { data, loading, error } = useQuery(ScoresDocument)
  if (loading) return <Box>ロード中...</Box>;
  if (error) return <Box>{error.message}</Box>;
  return (
    <Box>
      <ul>
        {data.scores.map((score: Score) => (
          <li>{score.title}</li>
        ))}
      </ul>
    </Box>
  )
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      {page}
    </Layout>
  )
}

export default Page

queryもScoresDocmentという形で書き換えられ、前回自力で作成したScoreの型定義もサーバーサイドのschemaを使用して生成されたものなので、型づけの安全性が高まっています!

今回はここまで!

参考にさせていただいたサイト・記事

graphql-code-generator
初めての GraphQL。Code Generator で型を生成するまでのメモ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?