こちらの記事の派生。
コピペで別モジュールを作りました。
フォルダ構成
前回記事で「recipes」を作ったので、それをコピペして「items」って名前に変えてます。
「parts.resolver.ts」と「part.ts」は、同一モジュール内に複数resolverの作成したものです。
実装
item
item.ts
- クラス名変えただけで、コピペのまんま
import { Field, ID, ObjectType} from '@nestjs/graphql';
@ObjectType()
export class Item {
@Field(type => ID)
id: string;
@Field()
name: string;
}
items.module.ts
- 「autoSchemaFile」をtrueに変更、モジュール別にファイル分けると、別のモジュールの参照ができなかったため
- itemを他で使わせるため、exportsで使いたいサービスを設定
import { Module } from '@nestjs/common';
import { ItemsResolver } from './items.resolver';
import { ItemsService } from './items.service';
import { GraphQLModule } from '@nestjs/graphql';
import { PartsResolver } from './parts.resolver';
@Module({
providers: [ItemsResolver, ItemsService, PartsResolver],
exports: [ItemsService],
imports: [
GraphQLModule.forRoot({
installSubscriptionHandlers: true,
autoSchemaFile: true,
}),
],
})
export class ItemsModule {}
items.resolver.ts
- item取得の実装、単純にクエリ用
import { Resolver, Args, Query, ResolveField, Parent } from '@nestjs/graphql';
import { ItemsService } from './items.service';
import { Item } from './models/item';
@Resolver(of => Item)
export class ItemsResolver {
constructor(private readonly itemsService: ItemsService) {}
@Query(returns => Item)
async item(@Args('id') id: string): Promise<Item> {
return await this.itemsService.findOneById(id);
}
}
recipe
recipe.ts
- 今回追加した、itemsのカラムを追加
import { Field, ID, ObjectType} from '@nestjs/graphql';
import { Item } from '../../items/models/item';
@ObjectType()
export class Recipe {
@Field(type => ID)
id: string;
@Field()
name: string;
@Field(type => [Item], {nullable: "items"})
items?: Item[];
}
recipes.module.ts
- importsに「ItemModule」を追加
- autoSchemaFileを修正
import { Module } from '@nestjs/common';
import { RecipesResolver } from './recipes.resolver';
import { RecipesService } from './recipes.service';
import { ItemsModule } from '../items/items.module';
import { GraphQLModule } from '@nestjs/graphql';
@Module({
providers: [RecipesResolver, RecipesService],
imports: [
ItemsModule,
GraphQLModule.forRoot({
installSubscriptionHandlers: true,
autoSchemaFile: true,
}),
],
})
export class RecipesModule {}
recipes.resolver.ts
- 別モジュールのサービスをコンストラクタでDI
- 追加したitemsカラム用の取得ロジック追加(ResolveField)
- ResolveFieldに引数ないと、func名が自動で紐づくっぽい
- function名と分けたい時は、コメント化している指定方法で
import { Resolver, Args, Query, ResolveField, Parent } from "@nestjs/graphql";
import { RecipesService } from './recipes.service';
import { Recipe } from './models/recipe';
import { ItemsService } from '../items/items.service';
@Resolver(of => Recipe)
export class RecipesResolver {
constructor(
private readonly recipesService: RecipesService,
private readonly itemService: ItemsService,
) {}
@Query(returns => Recipe)
async recipe(@Args('id') id: string): Promise<Recipe> {
return await this.recipesService.findOneById(id);
}
// @ResolveField('items', returns => [Item])
@ResolveField()
async items(@Parent() parent: Recipe) {
return [await this.itemService.findOneByRecipeId(parent.id)];
}
}
これで、モジュールの追加および、参照がいける。
ちなみにこの「ResolveField」、大元のクエリ取得件数が多い場合、その件数分呼ばれる。そのため多い場合はとしてDataLoaderってのが必要そう。
参考
parts
- 一応おまけで記載
part.ts
- 同一モジュールのitemを参照する形
import { Field, ID, ObjectType} from '@nestjs/graphql';
import { Item } from "./item";
@ObjectType()
export class Part {
@Field(type => ID)
id: string;
@Field()
name: string;
@Field(type => [Item], {nullable: "items"})
items?: Item[];
}
parts.resolver.ts
import { Resolver, Args, Query, ResolveField, Parent } from '@nestjs/graphql';
import { ItemsService } from './items.service';
import { Part } from './models/part';
@Resolver(of => Part)
export class PartsResolver {
constructor(private readonly itemsService: ItemsService) {}
@Query(returns => Part)
async parts(@Args('id') id: string): Promise<Part> {
return await this.itemsService.findOneById(id);
}
// @ResolveField('items', returns => [Item])
@ResolveField()
async items(@Parent() parent: Part) {
return [await this.itemsService.findOneByRecipeId(parent.id)];
}
}
※Resolverが違うのならば、ResolveFieldは同じfunc名で良さげ。
検証
query GetRecipe {
recipe(id: "1") {
id
name
items {
id
name
}
}
}
{
"data": {
"recipe": {
"id": "1234",
"name": "name",
"items": [
{
"id": "recipe12345",
"name": "recipe_name"
}
]
}
}
}
まとめ
こんな感じでResolver毎に作っていけば、開発は進めそう。
ServiceとResolverで開発分けれるし、規模によっていは分担とかもありかも。
あとはMutationとSubscriptionですが、それは下記の公式?サンプルを見てもらえればって感じです。
