Subscriptions

Subscriptions

Subscription์€ Query ๋ฐ Mutation๊ณผ ๊ฐ™์€ ๋˜ ๋‹ค๋ฅธ GraphQL ์ž‘์—… ์œ ํ˜•์ž…๋‹ˆ๋‹ค. ์–‘๋ฐฉํ–ฅ ์ „์†ก ๊ณ„์ธต, ์ฃผ๋กœ ์›น ์†Œ์ผ“์„ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ๊ตฌ๋…์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Subscription์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ์•„๋ž˜๋Š” ๊ณต์‹ Apollo ๋ฌธ์„œ์—์„œ ์ง์ ‘ ๋ณต์‚ฌ ํ•œ commentAdded ๊ตฌ๋… ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

Subscription: {
  commentAdded: {
    subscribe: () => pubSub.asyncIterator('commentAdded');
  }
}

warning ์•Œ๋ฆผ pubsub๋Š” PubSub ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

Schema first

Nest์—์„œ ๋™๋“ฑํ•œ ๊ตฌ๋…์„ ๋งŒ๋“ค๋ ค๋ฉด @Subscription()๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const pubSub = new PubSub();

@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 });
  }

  @Subscription()
  commentAdded() {
    return pubSub.asyncIterator('commentAdded');
  }
}

์ปจํ…์ŠคํŠธ์™€ ์ธ์ˆ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ธฐ ์œ„ํ•ด filter์†์„ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Subscription('commentAdded', {
  filter: (payload, variables) =>
    payload.commentAdded.repositoryName === variables.repoFullName,
})
commentAdded() {
  return pubSub.asyncIterator('commentAdded');
}

๊ฒŒ์‹œ๋œ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด resolve ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Subscription('commentAdded', {
  resolve: value => value,
})
commentAdded() {
  return pubSub.asyncIterator('commentAdded');
}

Type definitions

๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋Š” ์œ ํ˜• ์ •์˜ ํŒŒ์ผ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

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

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

type Comment {
  id: String
  content: String
}

type Subscription {
  commentAdded(repoFullName: String!): Comment
}

์ž˜ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ํ•˜๋‚˜์˜ commentAdded(repoFullName: String!): Comment ๊ฐ€์ž…์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ „์ฒด ์ƒ˜ํ”Œ ๊ตฌํ˜„์€ ์—ฌ๊ธฐ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Code first

ํด๋ž˜์Šค ์šฐ์„  ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ๋…์„ ๋งŒ๋“ค๋ ค๋ฉด @Subscription() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const pubSub = new PubSub();

@Resolver('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 });
  }

  @Subscription(returns => Comment)
  commentAdded() {
    return pubSub.asyncIterator('commentAdded');
  }
}

์ปจํ…์ŠคํŠธ์™€ ์ธ์ˆ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ธฐ ์œ„ํ•ด filter์†์„ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Subscription(returns => Comment, {
  filter: (payload, variables) =>
    payload.commentAdded.repositoryName === variables.repoFullName,
})
commentAdded() {
  return pubSub.asyncIterator('commentAdded');
}

๊ฒŒ์‹œ๋œ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉดresolve ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Subscription(returns => Comment, {
  resolve: value => value,
})
commentAdded() {
  return pubSub.asyncIterator('commentAdded');
}

PubSub

์—ฌ๊ธฐ์„œ๋Š” ๋กœ์ปฌ PubSub ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ , PubSub๋ฅผ ๊ณต๊ธ‰์ž๋กœ ์ •์˜ํ•˜๊ณ  ์ƒ์„ฑ์ž (@inject()๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ)๋ฅผ ํ†ตํ•ด ์ฃผ์ž…ํ•œ ๋‹ค์Œ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์žฌ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Nest ์‚ฌ์šฉ์ž ์ง€์ • ๊ณต๊ธ‰์ž ์—ฌ๊ธฐ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  provide: 'PUB_SUB',
  useValue: new PubSub(),
}

Module

Subscription์„ ๊ฐ€๋Šฅํ•˜๊ฒŒํ•˜๋ ค๋ฉด installSubscriptionHandlers ์†์„ฑ์„ true๋กœ ์„ค์ •ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

GraphQLModule.forRoot({
  typePaths: ['./**/*.graphql'],
  installSubscriptionHandlers: true,
}),

Subscription ์„œ๋ฒ„ (์˜ˆ : ํฌํŠธ ๋ณ€๊ฒฝ)๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•˜๋ ค๋ฉด subscriptions ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค (์—ฌ๊ธฐ).

Last updated

Was this helpful?