UnionsとEmums(列挙型)

Union

GraphQLにおけるUnion型はよくInterfaceに特徴が類似しています。しかし、型の間で共通のフィールドを指定できません。
Union型(以下、ユニオン型と表記)は1つのフィールドからバラバラのデータ型を返すのに便利です。

コードファースト

GraphQLのユニオン型を定義するには、このユニオンが構成されるクラスを定義しなければなりません。まず最初に、BookAuthorという2つのクラスを作成します。
import { Field, ObjectType } from '@nestjs/graphql';
// Book
@ObjectType()
export class Book {
@Field()
title: string;
}
// Author
@ObjectType()
export class Author {
@Field()
name: string;
}
上記の状態で、@nestjs/graphqlパッケージからエクスポートされたcreateUnionType関数を使用してResultUnionユニオンを登録します。
export const ResultUnion = createUnionType({
name: 'ResultUnion',
types: () => [Author, Book] as const,
});
注意
createUnionType関数のtypesプロパティで返される配列には、constを付与する必要があります。constが与えられていないと、コンパイル時に誤った宣言ファイルが生成され、他のプロジェクトから使用する際にエラーとなります。
これで、クエリの中でResultUnionを参照できるようになりました。
@Query(returns => [ResultUnion])
search(): Array<typeof ResultUnion> {
return [new Author(), new Book()];
}
これにより、SDLでGraphQLスキーマの以下の部分が生成される。
type Author {
name: String!
}
type Book {
title: String!
}
union ResultUnion = Author | Book
type Query {
search: [ResultUnion!]!
}
ライブラリが生成するデフォルトのresolveType関数は、resolverメソッドから返された値を基に型を抽出します。つまり、リテラルなJavaScriptオブジェクトではなく、クラスインスタンスを返すことが義務付けられているのです。
カスタマイズされたresolveType()関数を提供するには、以下のようにcreateUnionType関数に渡されるオプション・オブジェクトに resolveTypeプロパティを渡してください。
export const ResultUnion = createUnionType({
name: 'ResultUnion',
types: () => [Author, Book] as const,
resolveType(value) {
if (value.name) {
return Author;
}
if (value.title) {
return Book;
}
return null;
},
});

スキーマファースト

スキーマファーストのアプローチでユニオンを定義するには、SDLでGraphQLユニオンを作成するだけで構いません。
type Author {
name: String!
}
type Book {
title: String!
}
union ResultUnion = Author | Book
そして、型付け生成機能(環境構築のセクションで紹介した)を用いて、対応するTypeScriptの定義を生成できます。
export class Author {
name: string;
}
export class Book {
title: string;
}
export type ResultUnion = Author | Book;
ユニオンはリゾルバマップにおいて、ユニオンがどのタイプに解決されるべきかを決定するために、余分な__resolveTypeフィールドを必要とします。また、ResultUnionResolverクラスは、どのモジュールにもプロバイダとして登録されなければならないことに注意してください。ResultUnionResolverクラスを作成し、__resolveTypeメソッドを定義してみましょう。
@Resolver('ResultUnion')
export class ResultUnionResolver {
@ResolveField()
__resolveType(value) {
if (value.name) {
return 'Author';
}
if (value.title) {
return 'Book';
}
return null;
}
}
メモ
すべてのデコレータは@nestjs/graphqlパッケージからエクスポートされる。

Enums(列挙型)

列挙型は、特定の値のグループ内に制限された特殊なスカラーです。これにより、主に以下のことが可能になります。
  • この型の引数が与えられている値のいずれかであることを確認すること
  • フィールドが常に有限の値の集合の1つであることを、型システムを通じて伝達すること

コードファースト

コードファーストのアプローチを使用する場合、TypeScriptのenumを作るだけでGraphQLのenum型を定義できます。
export enum AllowedColor {
RED,
GREEN,
BLUE,
}
この状態で、@nestjs/graphqlパッケージからエクスポートされたregisterEnumType関数を活用してAllowedColor型のenumを登録していきます。
registerEnumType(AllowedColor, {
name: 'AllowedColor',
});
これで、AllowedColorを型の中で参照できるようになりました。
@Field(type => AllowedColor)
favoriteColor: AllowedColor;
これにより、SDLでGraphQLスキーマの以下の部分が生成されます。
enum AllowedColor {
RED
GREEN
BLUE
}
enumの説明を記述するには、registerEnumType関数にdescriptionプロパティを渡します。
registerEnumType(AllowedColor, {
name: 'AllowedColor',
description: 'The supported colors.',
});
enumの値に説明をつけたり、値を非推奨としてマークするには、以下のようにvaluesMapプロパティを指定します。
registerEnumType(AllowedColor, {
name: 'AllowedColor',
description: 'The supported colors.',
valuesMap: {
RED: {
description: 'The default color.',
},
BLUE: {
deprecationReason: 'Too blue.',
},
},
});
このプログラムの場合、SDLで以下のようなGraphQLスキーマが生成されます。
"""
The supported colors.
"""
enum AllowedColor {
"""
The default color.
"""
RED
GREEN
BLUE @deprecated(reason: "Too blue.")
}

スキーマファースト

スキーマファーストのアプローチでenumを定義するには、SDLでGraphQLのenumを作成するだけです。
enum AllowedColor {
RED
GREEN
BLUE
}
そして、型付け生成機能を使って、対応するTypeScriptの定義を次のように作成できます。
export enum AllowedColor {
RED
GREEN
BLUE
}
バックエンドが、内部的に公開APIと異なる値をenumに強制することがあります。この例では、APIにREDが含まれているが、リゾルバでは代わりに#f00を使用することができます。これを実現するには、AllowedColorenum用のリゾルバオブジェクトを宣言します。
export const allowedColorResolver: Record<keyof typeof AllowedColor, any> = {
RED: '#f00',
};
メモ
全てのデコレータは@nestjs/graphqlパッケージからエクスポートされます。
そして、このリゾルバオブジェクトをGraphQLModule#forRoot()メソッドのresolversプロパティと一緒に、以下のように使用します。
GraphQLModule.forRoot({
resolvers: {
AllowedColor: allowedColorResolver,
},
});