5
1

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.

GraphQL Code GeneratorでTypeScriptのDateを扱う

Last updated at Posted at 2022-12-17

この記事の目的

TypeScriptでGraphQL Code Generatorを使うとき、Date関連の型をどう扱えばいいかすぐわかる記事がなかったので少し手間取りました。
わかってしまえばどうと言ったことはなかったのですが、なかなか辿り着けなかったので備忘録として残します。

環境

"graphql": "^16.6.0",
"graphql-scalars": "^1.20.1",
"graphql-yoga": "^3.1.1",
"@graphql-codegen/cli": "2.16.1",

最終版のレポジトリ

GraphQL Code Generaor

GraphQL Code GeneraorはGraphQLのSDLから関連するコードを生成してくれます。
今回はバックエンドのコード生成が主な目的なので、GraphQL Code Generatorの設定ファイルである
codegen.tsplugins'typescript-resolvers'を指定します。

codegen.ts

const config: CodegenConfig = {
  schema: 'schema.graphql',
    generates: {
      './src/resolvers-types.ts': {    
        plugins: ['typescript', 'typescript-resolvers'],
      },
    },
    ...
  }
}

そのように設定するとyarn graphql-codegenで下のようなGraphQLのSDLから、Resolverを含む型定義のファイルを生成してくれます。

scheme.graphql
type Task {
  id: ID
  content: String!
  hasDone: Boolean!
}
type Query {
  drafts: [Task!]
}
resolvers-types.ts
...
export type Query = {
  __typename?: "Query";
  drafts: Maybe<Array<Task>>;
};

export type Task = {
  __typename?: "Task";
  content: Scalars["String"];
  hasDone: Scalars["Boolean"];
  id: Maybe<Scalars["ID"]>;
};

...
export type TaskResolvers<
  ContextType = any,
  ParentType extends ResolversParentTypes["Task"] = ResolversParentTypes["Task"]
> = {
  content?: Resolver<ResolversTypes["String"], ParentType, ContextType>;
  hasDone?: Resolver<ResolversTypes["Boolean"], ParentType, ContextType>;
  id?: Resolver<Maybe<ResolversTypes["ID"]>, ParentType, ContextType>;
  __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
...

この時にResolverで以下のように指定すると生成した型を元にResolverを実装できます。

task.ts
export const TaskResolever: QueryResolvers['drafts'] = () => {
   /// Taskを返す処理
}

下の画像のように型を元に自動補完をすることも可能です。
image.png

Date関連のプロパティをGraphQLのSDLで定義

GraphQLのデフォルトのScalar値は下記のものしか用意されていません。

  • Int
  • Float
  • String
  • Boolean
  • ID

そのため、DateやDateTimeなどを扱いたい時にはCustom Scalarとして追加する必要があります。
Custom Scalarは以下のように定義します。

schema.graphql
scalar DateTime # この部分
scalar Date     # この部分

type Task {
  id: ID
  content: String!
  hasDone: Boolean!
  createdAt: DateTime! # Custom Scalarを参照
  createdOn: Date!     # Custom Scalarを参照
}
type Query {
 drafts: [Task!]
}

ただし、このように指定してもGraphQL Code Generator側ではこのCustom Scalarの型を参照できないため、
TypeScriptで参照するときにCustom Scalarがany型になってしまいます。
これでは型生成をしている意味がありません。
image.png

CutomScalarにTypeScriptの型を設定するには、codegen.tsでDateとDateTimeの型を指定してあげる必要があります。
ここの定義の仕方は型のオブジェクトではなく、文字列である点に注意が必要です
つまりDateではなく'Date'を指定します。

codegen.ts
...
const config: CodegenConfig = {
    ...
    scalars: {
      DateTime: 'Date',
      Date: 'Date'
    }
  }
};
...

そうすると、ResolverからもDate型であることを参照でき、型チェックが行われます。
image.png

このようにCustom Scalarがany型になってしまうことを防ぎたい場合、strictScalarsという項目をtrueに設定しておくと、コード生成時にCustom Scalarの型がanyの時にエラーを吐いてくれます。

codegen.ts
...
const config: CodegenConfig = {
    ...
    strictScalars: true, // この部分を追加
    scalars: {} // Date, DateTimeの定義がない場合
  }
};
...

image.png

GraphQL Scalars

TypeScriptの場合、よく使うCustom ScalarはGraphQL Scalarsというパッケージが用意されており、DateやDateTimeもこちらの定義を利用することができます。

上のschema.graphqlではansweredAtはDateTime型、answeredOnはDate型を指定しているのに、TypeScriptのコードではどちらもDate型で返していました。

特に設定をしなければ上記の実装では下のようなレスポンスが返ります。
image.png

どちらもnew Date().toString()の値が返っているように見えます。
期待された通りの値を返すためにはGraphQL Scalarsの型定義とResolverを追加する必要があります。

image.png

そうすると、Date型を指定していた部分にはyyyy-MM-ddの値が返るようになります。
image.png

終わりに

GraphQL Code GeneratorでDate、DateTimeを扱う場合、GraphQL Scalarsを使うと、自分でフォーマットを定義する必要がなく使えてとても便利ですが
公式ドキュメントからこれらの使い方を理解するのはなかなか難しかったため、同じようなところで詰まっている方の助けになれば嬉しいです。

追記

以下の理由で一旦GraphQL Code Generatorを扱うのはお休みします。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?