きみはねこみたいなにゃんにゃんなまほう

ねこもスクリプトをかくなり

GraphQL.js で Custom Scalars を定義する - まずは serialize から

(=˘ ꒳ ˘=) GraphQL.js の TypeScript の定義を眺めながらなんとなくな雰囲気で Custom Scalar を定義してみる

...GraphQL.js の GraphQLScalarType を使います。コンストラクタの引数は以下のようになっています。

export interface GraphQLScalarTypeConfig<TInternal, TExternal> {
  name: string;
  description?: string;
  astNode?: ScalarTypeDefinitionNode;
  serialize(value: any): TExternal | null | undefined;
  parseValue?(value: any): TInternal | null | undefined;
  parseLiteral?(valueNode: ValueNode): TInternal | null | undefined;
}

とりあえず nameserialize が最低限必要みたいですね。

シリアライズで何もしない

現在時刻の Unix 秒をソースにして graphql を実行する処理を書いてみましょう。まずはシリアライズで何もしないようにします。

import {
  graphql,
  GraphQLScalarType,
  GraphQLObjectType,
  GraphQLSchema,
} from 'graphql'

const GraphQLDate = new GraphQLScalarType({
  name: 'Date',
  serialize(value) {
    // 何もしない (*˘꒳˘*) ただ返すだけ...
    return value
  },
})

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'rootQuery',
    fields: {
      now: {
        type: GraphQLDate,
      },
    },
  }),
})

const query = `{ now }`

const rootValue = {
  // 現在時刻をUnix秒で返す
  now: () => Date.now(),
}

graphql(schema, query, rootValue).then(console.log, console.error)

これを実行すると

{ data: { now: 1518864848888 } }

と、Unix 秒がそのまま取得されるのがわかります。

シリアライズで変換処理

次にシリアライズUnix 秒からISOの時刻文字列に変換する処理に変更してみます。

const GraphQLDate = new GraphQLScalarType({
  name: 'Date',
  serialize(value) {
    // もう何もしないぼくじゃない (*˘꒳˘*) ただ返すだけとか無能
    return new Date(value).toISOString()
  },
})

とすると

{ data: { now: '2018-02-17T10:56:22.882Z' } }

ISOの時刻文字列に変換された値が取得できます。

という感じで GraphQL.js で Custom Scalars を使う方法がなんとなくわかりましたね。

GraphQL で時刻を扱う

GraphQL で時刻を扱う場合、現在メジャーなパッケージは graphql-iso-date になるでしょうか。 参考までに覗いてみると型定義は src/dateTime/index.js に存在します。基本的な定義は大体読めるようになりましたが、今回触れなかった parseLiteralparseValue といった定義が serialize の他にも行われていますね。

また調べていきたいところです。

余談

graphql を実行している以下の部分

const query = `{ now }`

const rootValue = {
  now: () => Date.now(),
}

graphql(schema, query, rootValue).then(console.log, console.error)

サンプルコードとしてわかりやすく書きたかったので今回は丁寧に手前で変数を定義して実行していますが、もうすこし簡単に書けてしまいます。 rootValue の定義をよく見て見ると nowDate.now を呼んでいるだけなので Date をそのまま使っても同様のインタフェースを提供できますね。なので

graphql(schema, `{ now }`, Date).then(console.log, console.error)

のようにワンライナーで実行できてしまいます。