Resolvers

Resolvers

일반적으로 리졸버 맵을 수동으로 작성해야 합니다. 반면, @nestjs/graphql 패키지는 데코레이터가 제공하는 메타 데이터를 사용하여 리졸버 맵을 자동으로 생성합니다. 라이브러리 기본 사항을 배우기 위해 간단한 작성자 API를 작성합니다.

Schema first

이전 챕터에서 언급했듯이, 스키마 첫번째 접근 방식에서는 SDL에서 유형을 수동으로 정의해야합니다 (여기).

type Author {
  id: Int!
  firstName: String
  lastName: String
  posts: [Post]
}

type Post {
  id: Int!
  title: String!
  votes: Int
}

type Query {
  author(id: Int!): Author
}

GraphQL 스키마에는 author(id: Int!): Author라는 단일 쿼리가 포함되어 있습니다. 이제 AuthorResolver를 만들어 보자.

@Resolver('Author')
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query()
  async author(@Args('id') id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty()
  async posts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}

info 힌트 @Resolver()데코레이터를 사용하는 경우 클래스를 @Injectable()으로 표시할 필요가 없습니다. 그렇지 않으면 필요합니다.

@Resolver()데코레이터는 쿼리와 돌연변이 (@Query()@Mutation()데코레이터 모두)에 영향을 미치지 않습니다. 이 특정 클래스 내부의 각 @ResolveProperty()에는 부모 (이 경우에는 Author 유형) (Author.posts관계)가 있음을 Nest에게만 알려줍니다. 기본적으로 클래스 상단에@Resolver()를 설정하는 대신 메소드에 가깝게 수행할 수 있습니다.

@Resolver('Author')
@ResolveProperty()
async posts(@Parent() author) {
  const { id } = author;
  return await this.postsService.findAll({ authorId: id });
}

그러나 하나의 클래스 안에 여러 개의 @ResolveProperty()가 있다면, 반드시 @Resolver()를 추가해야 합니다.

일반적으로 우리는 메소드 이름으로 getAuthor() 또는 getPosts()와 같은 것을 사용합니다. 데코레이터의 괄호 사이에서 실제 이름을 이동하면 쉽게 할 수 있습니다.

@Resolver('Author')
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query('author')
  async getAuthor(@Args('id') id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty('posts')
  async getPosts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}

info 힌트 @Resolver()데코레이터는 메소드 수준에서도 사용할 수 있습니다.

Typings

이전 챕터에서 타이핑 생성 기능 (outputAs: 'class' 포함)을 활성화 했다고 가정하면 응용 프로그램을 실행하면 다음 파일이 생성됩니다.

export class Author {
  id: number;
  firstName?: string;
  lastName?: string;
  posts?: Post[];
}

export class Post {
  id: number;
  title: string;
  votes?: number;
}

export abstract class IQuery {
  abstract author(id: number): Author | Promise<Author>;
}

클래스를 사용하면 데코레이터를 사용하여 유효성 검사 목적에서 매우 유용합니다 (여기 참조). 예를 들면 다음과 같습니다.

import { MinLength, MaxLength } from 'class-validator';

export class CreatePostInput {
  @MinLength(3)
  @MaxLength(50)
  title: string;
}

warning 알림 입력 및 파라미터의 자동 유효성 검사를 활성화하려면 ValidationPipe를 사용해야합니다. 유효성 검사 여기 또는 파이프 여기에 대해 자세히 알아보십시오.

그럼에도 불구하고 데코레이터를 자동으로 생성된 파일에 직접 추가하면 연속된 각 변경에 따라 제거됩니다. 따라서 별도의 파일을 작성하고 생성된 클래스를 간단히 확장해야합니다.

import { MinLength, MaxLength } from 'class-validator';
import { Post } from '../../graphql.ts';

export class CreatePostInput extends Post {
  @MinLength(3)
  @MaxLength(50)
  title: string;
}

Code first

코드 우선 접근 방식에서는 SDL을 직접 작성할 필요가 없습니다. 대신 데코레이터 만 사용합니다.

import { Field, Int, ObjectType } from 'type-graphql';
import { Post } from './post';

@ObjectType()
export class Author {
  @Field(type => Int)
  id: number;

  @Field({ nullable: true })
  firstName?: string;

  @Field({ nullable: true })
  lastName?: string;

  @Field(type => [Post])
  posts: Post[];
}

Author모델이 생성되었습니다. 이제 누락된 Post 클래스를 만들어 봅시다.

import { Field, Int, ObjectType } from 'type-graphql';

@ObjectType()
export class Post {
  @Field(type => Int)
  id: number;

  @Field()
  title: string;

  @Field(type => Int, { nullable: true })
  votes?: number;
}

모델이 준비되었으므로 리졸버 클래스로 이동할 수 있습니다.

@Resolver(of => Author)
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query(returns => Author)
  async author(@Args({ name: 'id', type: () => Int }) id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty()
  async posts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}

일반적으로 우리는 메소드 이름으로 getAuthor() 또는 getPosts()와 같은 것을 사용합니다. 실명을 데코레이터로 옮겨서 쉽게 할 수 있습니다.

@Resolver(of => Author)
export class AuthorResolver {
  constructor(
    private readonly authorsService: AuthorsService,
    private readonly postsService: PostsService,
  ) {}

  @Query(returns => Author, { name: 'author' })
  async getAuthor(@Args({ name: 'id', type: () => Int }) id: number) {
    return await this.authorsService.findOneById(id);
  }

  @ResolveProperty('posts')
  async getPosts(@Parent() author) {
    const { id } = author;
    return await this.postsService.findAll({ authorId: id });
  }
}

일반적으로 이러한 객체를 @Args()데코레이터로 전달할 필요는 없습니다. 예를 들어, 식별자 유형이 문자열이면 다음과 같이 구성하면됩니다.

@Args('id') id: string

그러나 숫자유형은 예상되는 GraphQL 표현 (IntFloat)에 대한 충분한 정보를 type-graphql에 제공하지 않으므로 타잎 레퍼런스를 명시적으로 전달해야 합니다.

또한 전용 AuthorArgs클래스를 만들 수 있습니다.

@Args() id: AuthorArgs

다음 본문으로:

@ArgsType()
class AuthorArgs {
  @Field(type => Int)
  @Min(1)
  id: number;
}

info 힌트 @Field()@ArgsType()데코레이터는 모두 type-graphql 패키지에서 가져오고, @Min()은 클래스 유효성 검사기에서 가져옵니다.

또한 이러한 클래스는 ValidationPipe(여기 읽기)에서 매우 잘 작동합니다.

Decorators

전용 데코레이터를 사용하여 다음과 같은 주장을 언급할 수 있습니다. 아래는 제공된 데코레이터와 이들이 나타내는 일반 아폴로 파라미터를 비교한 것입니다.

@Root() and @Parent()

root/parent

@Context(param?: string)

context / context[param]

@Info(param?: string)

info / info[param]

@Args(param?: string)

args / args[param]

Module

여기에서 완료되면, 예를 들어 새로 작성된 AuthorsModule 내부에 AuthorResolver를 등록해야 합니다.

@Module({
  imports: [PostsModule],
  providers: [AuthorsService, AuthorResolver],
})
export class AuthorsModule {}

GraphQLModule은 메타 데이터를 반영하고 클래스를 올바른 리졸버 맵으로 자동 변환합니다. 알아야 할 유일한 것은 이 모듈을 어딘가에 가져와야 한다는 것입니다. 따라서 Nest는 AuthorsModule이 실제로 존재함을 알게됩니다.

info 힌트 GraphQL 쿼리에 대한 자세한 내용은 여기를 참조하십시오.

Last updated