GraphQL15 min read

Subscriptionlar

Querylar orqali ma'lumot olish va mutatsiyalar orqali ma'lumotni o'zgartirishdan tashqari, GraphQL spetsifikatsiyasi subscription deb ataladigan uchinchi operatsiya turini ham qo'l

Querylar orqali ma'lumot olish va mutatsiyalar orqali ma'lumotni o'zgartirishdan tashqari, GraphQL spetsifikatsiyasi subscription deb ataladigan uchinchi operatsiya turini ham qo'llab-quvvatlaydi. GraphQL subscriptionlari - serverdan real time xabarlarni tinglashni tanlagan klientlarga serverdan ma'lumot yuborish usuli. Subscriptionlar querylarga o'xshaydi: ular klientga yetkaziladigan fieldlar to'plamini belgilaydi, ammo bitta javobni darhol qaytarish o'rniga kanal ochiladi va serverda muayyan hodisa sodir bo'lganda har safar natija klientga yuboriladi.

Subscriptionlar uchun keng tarqalgan use case - klient tomonni muayyan hodisalar haqida xabardor qilish, masalan yangi obyekt yaratilishi, fieldlar yangilanishi va h.k. (batafsil bu yerda).

Apollo driver bilan subscriptionlarni yoqish

Subscriptionlarni yoqish uchun installSubscriptionHandlers xossasini true qiling.

TypeScript
1GraphQLModule.forRoot<ApolloDriverConfig>({
2  driver: ApolloDriver,
3  installSubscriptionHandlers: true,
4}),
Warning

installSubscriptionHandlers konfiguratsiya opsiyasi Apollo serverning so'nggi versiyasidan olib tashlangan va bu paketda ham tez orada deprecate qilinadi. Default holatda installSubscriptionHandlerssubscriptions-transport-wsga fallback qiladi (batafsil), ammo biz graphql-ws (batafsil) kutubxonasidan foydalanishni qat'iy tavsiya qilamiz.

Buning o'rniga graphql-ws paketidan foydalanish uchun quyidagi konfiguratsiyani ishlating:

TypeScript
1GraphQLModule.forRoot<ApolloDriverConfig>({
2  driver: ApolloDriver,
3  subscriptions: {
4    'graphql-ws': true
5  },
6}),
Hint

Orqaga moslik uchun subscriptions-transport-ws va graphql-ws ni bir vaqtda ishlatishingiz ham mumkin.

Code first

Code first yondashuvida subscription yaratish uchun @Subscription() dekoratoridan (@nestjs/graphql paketidan eksport qilinadi) va graphql-subscriptions paketidagi PubSub klassidan foydalanamiz; u oddiy publish/subscribe API taqdim etadi.

Quyidagi subscription handler PubSub#asyncIterableIterator chaqiruvi orqali hodisaga obuna bo'ladi. Bu metod bitta argument qabul qiladi - triggerName, ya'ni event topic nomi.

TypeScript
1const pubSub = new PubSub();
2
3@Resolver(() => Author)
4export class AuthorResolver {
5  // ...
6  @Subscription(() => Comment)
7  commentAdded() {
8    return pubSub.asyncIterableIterator('commentAdded');
9  }
10}
Hint

Barcha dekoratorlar @nestjs/graphql paketidan, PubSub klassi esa graphql-subscriptions paketidan eksport qilinadi.

Note

PubSub oddiy publish va subscribe API taqdim etadigan klass. Bu haqida bu yerda batafsil o'qing. Apollo hujjatlarida default implementatsiya production uchun mos emasligi haqida ogohlantiriladi (batafsil). Production ilovalarida tashqi storega tayanadigan PubSub implementatsiyasidan foydalanish kerak (batafsil).

Bu SDLda quyidagi qismini generatsiya qiladi:

Graphql
1type Subscription {
2  commentAdded(): Comment!
3}

Subscriptionlar ta'rifi bo'yicha bitta top-level xossaga ega obyekt qaytaradi; bu xossaning kaliti subscription nomi bo'ladi. Bu nom yoki subscription handler metodi nomidan olinadi (yuqorida commentAdded) yoki @Subscription() dekoratoriga ikkinchi argument sifatida name kalitli opsiya berib aniq ko'rsatiladi:

TypeScript
1@Subscription(() => Comment, {
2  name: 'commentAdded',
3})
4subscribeToCommentAdded() {
5  return pubSub.asyncIterableIterator('commentAdded');
6}

Bu konstruktsiya avvalgi kod namunasidagi kabi SDLni hosil qiladi, ammo metod nomini subscriptiondan ajratishga imkon beradi.

Publishing

Endi hodisani publish qilish uchun PubSub#publish metodidan foydalanamiz. Bu ko'pincha mutatsiya ichida, obyekt graphning bir qismi o'zgarganda klient tomondagi yangilanishni ishga tushirish uchun ishlatiladi. Masalan:

TypeScript
posts/posts.resolver
1@Mutation(() => Comment)
2async addComment(
3  @Args('postId', { type: () => Int }) postId: number,
4  @Args('comment', { type: () => Comment }) comment: CommentInput,
5) {
6  const newComment = this.commentsService.addComment({ id: postId, comment });
7  pubSub.publish('commentAdded', { commentAdded: newComment });
8  return newComment;
9}

PubSub#publish metodi birinchi parametr sifatida triggerName (event topic nomi) va ikkinchi parametr sifatida event payloadini qabul qiladi. Avval aytilganidek, subscription ta'rifi bo'yicha qiymat qaytaradi va bu qiymat ma'lum shaklga ega. commentAdded subscriptioni uchun generatsiya qilingan SDLga yana bir qarang:

Graphql
1type Subscription {
2  commentAdded(): Comment!
3}

Bu subscription commentAdded nomli top-level xossaga ega obyekt qaytarishi va bu xossa qiymati Comment obyektidan iborat bo'lishi kerakligini bildiradi. Muhim nuqta shuki, PubSub#publish orqali yuboriladigan event payloadi subscriptiondan qaytadigan qiymat shakliga mos bo'lishi kerak. Shuning uchun yuqoridagi misolda pubSub.publish('commentAdded', {{ '{' }} commentAdded: newComment {{ '}' }}) operatori mos shakldagi payload bilan commentAdded eventini publish qiladi. Agar bu shakllar mos kelmasa, subscription GraphQL validation bosqichida muvaffaqiyatsiz bo'ladi.

Subscriptionlarni filtrlash

Muayyan eventlarni filtrlash uchun filter xossasiga filter funksiyasini bering. Bu funksiya array filter ga o'xshash ishlaydi. U ikkita argument oladi: event payloadini o'z ichiga olgan payload (publish qilingan) va subscription so'rovida uzatilgan argumentlarni o'z ichiga olgan variables. Funksiya bu event klientlar uchun publish qilinishi kerakmi-yo'qligini belgilovchi boolean qaytaradi.

TypeScript
1@Subscription(() => Comment, {
2  filter: (payload, variables) =>
3    payload.commentAdded.title === variables.title,
4})
5commentAdded(@Args('title') title: string) {
6  return pubSub.asyncIterableIterator('commentAdded');
7}

Subscription payloadini o'zgartirish

Publish qilingan event payloadini o'zgartirish uchun resolve xossasiga funksiya bering. Funksiya event payloadini (publish qilingan) qabul qiladi va mos qiymatni qaytaradi.

TypeScript
1@Subscription(() => Comment, {
2  resolve: value => value,
3})
4commentAdded() {
5  return pubSub.asyncIterableIterator('commentAdded');
6}
Note

Agar resolve opsiyasidan foydalansangiz, unwrapped payloadni qaytarishingiz kerak (bizning misolda {{ '{' }} commentAdded: newComment {{ '}' }} emas, to'g'ridan-to'g'ri newComment obyektini qaytaring).

Agar injected providerlarga kirish kerak bo'lsa (masalan, ma'lumotni tekshirish uchun tashqi servisni chaqirish), quyidagi konstruktsiyadan foydalaning.

TypeScript
1@Subscription(() => Comment, {
2  resolve(this: AuthorResolver, value) {
3    // "this" refers to an instance of "AuthorResolver"
4    return value;
5  }
6})
7commentAdded() {
8  return pubSub.asyncIterableIterator('commentAdded');
9}

Xuddi shu konstruktsiya filtrlarda ham ishlaydi:

TypeScript
1@Subscription(() => Comment, {
2  filter(this: AuthorResolver, payload, variables) {
3    // "this" refers to an instance of "AuthorResolver"
4    return payload.commentAdded.title === variables.title;
5  }
6})
7commentAdded() {
8  return pubSub.asyncIterableIterator('commentAdded');
9}

Schema first

Nestda shunga teng subscription yaratish uchun @Subscription() dekoratoridan foydalanamiz.

TypeScript
1const pubSub = new PubSub();
2
3@Resolver('Author')
4export class AuthorResolver {
5  // ...
6  @Subscription()
7  commentAdded() {
8    return pubSub.asyncIterableIterator('commentAdded');
9  }
10}

Muayyan eventlarni context va argumentlar bo'yicha filtrlash uchun filter xossasini bering.

TypeScript
1@Subscription('commentAdded', {
2  filter: (payload, variables) =>
3    payload.commentAdded.title === variables.title,
4})
5commentAdded() {
6  return pubSub.asyncIterableIterator('commentAdded');
7}

Published payloadni o'zgartirish uchun resolve funksiyasidan foydalanamiz.

TypeScript
1@Subscription('commentAdded', {
2  resolve: value => value,
3})
4commentAdded() {
5  return pubSub.asyncIterableIterator('commentAdded');
6}

Agar injected providerlarga kirish kerak bo'lsa (masalan, ma'lumotni tekshirish uchun tashqi servisni chaqirish), quyidagi konstruktsiyadan foydalaning:

TypeScript
1@Subscription('commentAdded', {
2  resolve(this: AuthorResolver, value) {
3    // "this" refers to an instance of "AuthorResolver"
4    return value;
5  }
6})
7commentAdded() {
8  return pubSub.asyncIterableIterator('commentAdded');
9}

Xuddi shu konstruktsiya filtrlarda ham ishlaydi:

TypeScript
1@Subscription('commentAdded', {
2  filter(this: AuthorResolver, payload, variables) {
3    // "this" refers to an instance of "AuthorResolver"
4    return payload.commentAdded.title === variables.title;
5  }
6})
7commentAdded() {
8  return pubSub.asyncIterableIterator('commentAdded');
9}

Oxirgi qadam - type definitions faylini yangilash.

Graphql
1type Author {
2  id: Int!
3  firstName: String
4  lastName: String
5  posts: [Post]
6}
7
8type Post {
9  id: Int!
10  title: String
11  votes: Int
12}
13
14type Query {
15  author(id: Int!): Author
16}
17
18type Comment {
19  id: String
20  content: String
21}
22
23type Subscription {
24  commentAdded(title: String!): Comment
25}

Shu bilan biz bitta commentAdded(title: String!): Comment subscription yaratdik. To'liq namuna implementatsiyani bu yerda topasiz.

PubSub

Yuqoridagi misollarda default PubSub emitteridan foydalandik (mqemitter). Afzal yondashuv (production uchun) - mqemitter-redisdan foydalanish. Muqobil ravishda, custom PubSub implementatsiyasini ham berishingiz mumkin (batafsil).

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: {
4    emitter: require('mqemitter-redis')({
5      port: 6579,
6      host: '127.0.0.1',
7    }),
8  },
9});

WebSocket orqali autentifikatsiya

Foydalanuvchi autentifikatsiyadan o'tganini tekshirish subscription opsiyalarida ko'rsatiladigan verifyClient callback funksiyasi ichida bajarilishi mumkin.

verifyClient birinchi argument sifatida info obyektini oladi; undan so'rov headerlarini olish uchun foydalanishingiz mumkin.

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: {
4    verifyClient: (info, next) => {
5      const authorization = info.req.headers?.authorization as string;
6      if (!authorization?.startsWith('Bearer ')) {
7        return next(false);
8      }
9      next(true);
10    },
11  }
12}),

Mercurius driver bilan subscriptionlarni yoqish

Subscriptionlarni yoqish uchun subscription xossasini true qiling.

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: true,
4}),
Hint

Custom emitter sozlash, kiruvchi ulanishlarni validatsiya qilish va h.k. uchun opsiyalar obyektini ham uzatishingiz mumkin. Batafsil bu yerda o'qing (subscription bo'limi).

Code first

Code first yondashuvida subscription yaratish uchun @Subscription() dekoratoridan (@nestjs/graphql paketidan eksport qilinadi) va mercurius paketidagi PubSub klassidan foydalanamiz; u oddiy publish/subscribe API taqdim etadi.

Quyidagi subscription handler PubSub#asyncIterableIterator chaqiruvi orqali hodisaga obuna bo'ladi. Bu metod bitta argument qabul qiladi - triggerName, ya'ni event topic nomi.

TypeScript
1@Resolver(() => Author)
2export class AuthorResolver {
3  // ...
4  @Subscription(() => Comment)
5  commentAdded(@Context('pubsub') pubSub: PubSub) {
6    return pubSub.subscribe('commentAdded');
7  }
8}
Hint

Yuqoridagi misolda ishlatilgan barcha dekoratorlar @nestjs/graphql paketidan eksport qilinadi, PubSub klassi esa mercurius paketidan eksport qilinadi.

Note

PubSub oddiy publish va subscribe API taqdim etadigan klass. Ushbu bo'limda custom PubSub klassini qanday ro'yxatdan o'tkazish ko'rsatilgan.

Bu SDLda quyidagi qismini generatsiya qiladi:

Graphql
1type Subscription {
2  commentAdded(): Comment!
3}

Subscriptionlar ta'rifi bo'yicha bitta top-level xossaga ega obyekt qaytaradi; bu xossaning kaliti subscription nomi bo'ladi. Bu nom yoki subscription handler metodi nomidan olinadi (yuqorida commentAdded) yoki @Subscription() dekoratoriga ikkinchi argument sifatida name kalitli opsiya berib aniq ko'rsatiladi:

TypeScript
1@Subscription(() => Comment, {
2  name: 'commentAdded',
3})
4subscribeToCommentAdded(@Context('pubsub') pubSub: PubSub) {
5  return pubSub.subscribe('commentAdded');
6}

Bu konstruktsiya avvalgi kod namunasidagi kabi SDLni hosil qiladi, ammo metod nomini subscriptiondan ajratishga imkon beradi.

Publishing

Endi hodisani publish qilish uchun PubSub#publish metodidan foydalanamiz. Bu ko'pincha mutatsiya ichida, obyekt graphning bir qismi o'zgarganda klient tomondagi yangilanishni ishga tushirish uchun ishlatiladi. Masalan:

TypeScript
posts/posts.resolver
1@Mutation(() => Comment)
2async addComment(
3  @Args('postId', { type: () => Int }) postId: number,
4  @Args('comment', { type: () => Comment }) comment: CommentInput,
5  @Context('pubsub') pubSub: PubSub,
6) {
7  const newComment = this.commentsService.addComment({ id: postId, comment });
8  await pubSub.publish({
9    topic: 'commentAdded',
10    payload: {
11      commentAdded: newComment
12    }
13  });
14  return newComment;
15}

Avval aytilganidek, subscription ta'rifi bo'yicha qiymat qaytaradi va bu qiymat ma'lum shaklga ega. commentAdded subscriptioni uchun generatsiya qilingan SDLga yana bir qarang:

Graphql
1type Subscription {
2  commentAdded(): Comment!
3}

Bu subscription commentAdded nomli top-level xossaga ega obyekt qaytarishi va bu xossa qiymati Comment obyektidan iborat bo'lishi kerakligini bildiradi. Muhim nuqta shuki, PubSub#publish orqali yuboriladigan event payloadi subscriptiondan qaytadigan qiymat shakliga mos bo'lishi kerak. Shuning uchun yuqoridagi misolda pubSub.publish({{ '{' }} topic: 'commentAdded', payload: {{ '{' }} commentAdded: newComment {{ '}' }} {{ '}' }}) operatori mos shakldagi payload bilan commentAdded eventini publish qiladi. Agar bu shakllar mos kelmasa, subscription GraphQL validation bosqichida muvaffaqiyatsiz bo'ladi.

Subscriptionlarni filtrlash

Muayyan eventlarni filtrlash uchun filter xossasiga filter funksiyasini bering. Bu funksiya array filter ga o'xshash ishlaydi. U ikkita argument oladi: event payloadini o'z ichiga olgan payload (publish qilingan) va subscription so'rovida uzatilgan argumentlarni o'z ichiga olgan variables. Funksiya bu event klientlar uchun publish qilinishi kerakmi-yo'qligini belgilovchi boolean qaytaradi.

TypeScript
1@Subscription(() => Comment, {
2  filter: (payload, variables) =>
3    payload.commentAdded.title === variables.title,
4})
5commentAdded(@Args('title') title: string, @Context('pubsub') pubSub: PubSub) {
6  return pubSub.subscribe('commentAdded');
7}

Agar injected providerlarga kirish kerak bo'lsa (masalan, ma'lumotni tekshirish uchun tashqi servisni chaqirish), quyidagi konstruktsiyadan foydalaning.

TypeScript
1@Subscription(() => Comment, {
2  filter(this: AuthorResolver, payload, variables) {
3    // "this" refers to an instance of "AuthorResolver"
4    return payload.commentAdded.title === variables.title;
5  }
6})
7commentAdded(@Args('title') title: string, @Context('pubsub') pubSub: PubSub) {
8  return pubSub.subscribe('commentAdded');
9}

Schema first

Nestda shunga teng subscription yaratish uchun @Subscription() dekoratoridan foydalanamiz.

TypeScript
1const pubSub = new PubSub();
2
3@Resolver('Author')
4export class AuthorResolver {
5  // ...
6  @Subscription()
7  commentAdded(@Context('pubsub') pubSub: PubSub) {
8    return pubSub.subscribe('commentAdded');
9  }
10}

Muayyan eventlarni context va argumentlar bo'yicha filtrlash uchun filter xossasini bering.

TypeScript
1@Subscription('commentAdded', {
2  filter: (payload, variables) =>
3    payload.commentAdded.title === variables.title,
4})
5commentAdded(@Context('pubsub') pubSub: PubSub) {
6  return pubSub.subscribe('commentAdded');
7}

Agar injected providerlarga kirish kerak bo'lsa (masalan, ma'lumotni tekshirish uchun tashqi servisni chaqirish), quyidagi konstruktsiyadan foydalaning:

TypeScript
1@Subscription('commentAdded', {
2  filter(this: AuthorResolver, payload, variables) {
3    // "this" refers to an instance of "AuthorResolver"
4    return payload.commentAdded.title === variables.title;
5  }
6})
7commentAdded(@Context('pubsub') pubSub: PubSub) {
8  return pubSub.subscribe('commentAdded');
9}

Oxirgi qadam - type definitions faylini yangilash.

Graphql
1type Author {
2  id: Int!
3  firstName: String
4  lastName: String
5  posts: [Post]
6}
7
8type Post {
9  id: Int!
10  title: String
11  votes: Int
12}
13
14type Query {
15  author(id: Int!): Author
16}
17
18type Comment {
19  id: String
20  content: String
21}
22
23type Subscription {
24  commentAdded(title: String!): Comment
25}

Shu bilan biz bitta commentAdded(title: String!): Comment subscription yaratdik.

PubSub

Yuqoridagi misollarda default PubSub emitteridan foydalandik (mqemitter). Afzal yondashuv (production uchun) - mqemitter-redisdan foydalanish. Muqobil ravishda, custom PubSub implementatsiyasini ham berishingiz mumkin (batafsil).

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: {
4    emitter: require('mqemitter-redis')({
5      port: 6579,
6      host: '127.0.0.1',
7    }),
8  },
9});

WebSocket orqali autentifikatsiya

Foydalanuvchi autentifikatsiyadan o'tganini tekshirish subscription opsiyalarida ko'rsatiladigan verifyClient callback funksiyasi ichida bajarilishi mumkin.

verifyClient birinchi argument sifatida info obyektini oladi; undan so'rov headerlarini olish uchun foydalanishingiz mumkin.

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: {
4    verifyClient: (info, next) => {
5      const authorization = info.req.headers?.authorization as string;
6      if (!authorization?.startsWith('Bearer ')) {
7        return next(false);
8      }
9      next(true);
10    },
11  }
12}),

Mercurius driver bilan subscriptionlarni yoqish

Subscriptionlarni yoqish uchun subscription xossasini true qiling.

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: true,
4}),
Hint

Custom emitter sozlash, kiruvchi ulanishlarni validatsiya qilish va h.k. uchun opsiyalar obyektini ham uzatishingiz mumkin. Batafsil bu yerda o'qing (subscription bo'limi).

Code first

Code first yondashuvida subscription yaratish uchun @Subscription() dekoratoridan (@nestjs/graphql paketidan eksport qilinadi) va mercurius paketidagi PubSub klassidan foydalanamiz; u oddiy publish/subscribe API taqdim etadi.

Quyidagi subscription handler PubSub#asyncIterableIterator chaqiruvi orqali hodisaga obuna bo'ladi. Bu metod bitta argument qabul qiladi - triggerName, ya'ni event topic nomi.

TypeScript
1@Resolver(() => Author)
2export class AuthorResolver {
3  // ...
4  @Subscription(() => Comment)
5  commentAdded(@Context('pubsub') pubSub: PubSub) {
6    return pubSub.subscribe('commentAdded');
7  }
8}
Hint

Yuqoridagi misolda ishlatilgan barcha dekoratorlar @nestjs/graphql paketidan eksport qilinadi, PubSub klassi esa mercurius paketidan eksport qilinadi.

Note

PubSub oddiy publish va subscribe API taqdim etadigan klass. Ushbu bo'limda custom PubSub klassini qanday ro'yxatdan o'tkazish ko'rsatilgan.

Bu SDLda quyidagi qismini generatsiya qiladi:

Graphql
1type Subscription {
2  commentAdded(): Comment!
3}

Subscriptionlar ta'rifi bo'yicha bitta top-level xossaga ega obyekt qaytaradi; bu xossaning kaliti subscription nomi bo'ladi. Bu nom yoki subscription handler metodi nomidan olinadi (yuqorida commentAdded) yoki @Subscription() dekoratoriga ikkinchi argument sifatida name kalitli opsiya berib aniq ko'rsatiladi:

TypeScript
1@Subscription(() => Comment, {
2  name: 'commentAdded',
3})
4subscribeToCommentAdded(@Context('pubsub') pubSub: PubSub) {
5  return pubSub.subscribe('commentAdded');
6}

Bu konstruktsiya avvalgi kod namunasidagi kabi SDLni hosil qiladi, ammo metod nomini subscriptiondan ajratishga imkon beradi.

Publishing

Endi hodisani publish qilish uchun PubSub#publish metodidan foydalanamiz. Bu ko'pincha mutatsiya ichida, obyekt graphning bir qismi o'zgarganda klient tomondagi yangilanishni ishga tushirish uchun ishlatiladi. Masalan:

TypeScript
posts/posts.resolver
1@Mutation(() => Comment)
2async addComment(
3  @Args('postId', { type: () => Int }) postId: number,
4  @Args('comment', { type: () => Comment }) comment: CommentInput,
5  @Context('pubsub') pubSub: PubSub,
6) {
7  const newComment = this.commentsService.addComment({ id: postId, comment });
8  await pubSub.publish({
9    topic: 'commentAdded',
10    payload: {
11      commentAdded: newComment
12    }
13  });
14  return newComment;
15}

Avval aytilganidek, subscription ta'rifi bo'yicha qiymat qaytaradi va bu qiymat ma'lum shaklga ega. commentAdded subscriptioni uchun generatsiya qilingan SDLga yana bir qarang:

Graphql
1type Subscription {
2  commentAdded(): Comment!
3}

Bu subscription commentAdded nomli top-level xossaga ega obyekt qaytarishi va bu xossa qiymati Comment obyektidan iborat bo'lishi kerakligini bildiradi. Muhim nuqta shuki, PubSub#publish orqali yuboriladigan event payloadi subscriptiondan qaytadigan qiymat shakliga mos bo'lishi kerak. Shuning uchun yuqoridagi misolda pubSub.publish({{ '{' }} topic: 'commentAdded', payload: {{ '{' }} commentAdded: newComment {{ '}' }} {{ '}' }}) operatori mos shakldagi payload bilan commentAdded eventini publish qiladi. Agar bu shakllar mos kelmasa, subscription GraphQL validation bosqichida muvaffaqiyatsiz bo'ladi.

Subscriptionlarni filtrlash

Muayyan eventlarni filtrlash uchun filter xossasiga filter funksiyasini bering. Bu funksiya array filter ga o'xshash ishlaydi. U ikkita argument oladi: event payloadini o'z ichiga olgan payload (publish qilingan) va subscription so'rovida uzatilgan argumentlarni o'z ichiga olgan variables. Funksiya bu event klientlar uchun publish qilinishi kerakmi-yo'qligini belgilovchi boolean qaytaradi.

TypeScript
1@Subscription(() => Comment, {
2  filter: (payload, variables) =>
3    payload.commentAdded.title === variables.title,
4})
5commentAdded(@Args('title') title: string, @Context('pubsub') pubSub: PubSub) {
6  return pubSub.subscribe('commentAdded');
7}

Agar injected providerlarga kirish kerak bo'lsa (masalan, ma'lumotni tekshirish uchun tashqi servisni chaqirish), quyidagi konstruktsiyadan foydalaning.

TypeScript
1@Subscription(() => Comment, {
2  filter(this: AuthorResolver, payload, variables) {
3    // "this" refers to an instance of "AuthorResolver"
4    return payload.commentAdded.title === variables.title;
5  }
6})
7commentAdded(@Args('title') title: string, @Context('pubsub') pubSub: PubSub) {
8  return pubSub.subscribe('commentAdded');
9}

Schema first

Nestda shunga teng subscription yaratish uchun @Subscription() dekoratoridan foydalanamiz.

TypeScript
1const pubSub = new PubSub();
2
3@Resolver('Author')
4export class AuthorResolver {
5  // ...
6  @Subscription()
7  commentAdded(@Context('pubsub') pubSub: PubSub) {
8    return pubSub.subscribe('commentAdded');
9  }
10}

Muayyan eventlarni context va argumentlar bo'yicha filtrlash uchun filter xossasini bering.

TypeScript
1@Subscription('commentAdded', {
2  filter: (payload, variables) =>
3    payload.commentAdded.title === variables.title,
4})
5commentAdded(@Context('pubsub') pubSub: PubSub) {
6  return pubSub.subscribe('commentAdded');
7}

Agar injected providerlarga kirish kerak bo'lsa (masalan, ma'lumotni tekshirish uchun tashqi servisni chaqirish), quyidagi konstruktsiyadan foydalaning:

TypeScript
1@Subscription('commentAdded', {
2  filter(this: AuthorResolver, payload, variables) {
3    // "this" refers to an instance of "AuthorResolver"
4    return payload.commentAdded.title === variables.title;
5  }
6})
7commentAdded(@Context('pubsub') pubSub: PubSub) {
8  return pubSub.subscribe('commentAdded');
9}

Oxirgi qadam - type definitions faylini yangilash.

Graphql
1type Author {
2  id: Int!
3  firstName: String
4  lastName: String
5  posts: [Post]
6}
7
8type Post {
9  id: Int!
10  title: String
11  votes: Int
12}
13
14type Query {
15  author(id: Int!): Author
16}
17
18type Comment {
19  id: String
20  content: String
21}
22
23type Subscription {
24  commentAdded(title: String!): Comment
25}

Shu bilan biz bitta commentAdded(title: String!): Comment subscription yaratdik.

PubSub

Yuqoridagi misollarda default PubSub emitteridan foydalandik (mqemitter). Afzal yondashuv (production uchun) - mqemitter-redisdan foydalanish. Muqobil ravishda, custom PubSub implementatsiyasini ham berishingiz mumkin (batafsil).

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: {
4    emitter: require('mqemitter-redis')({
5      port: 6579,
6      host: '127.0.0.1',
7    }),
8  },
9});

WebSocket orqali autentifikatsiya

Foydalanuvchi autentifikatsiyadan o'tganini tekshirish subscription opsiyalarida ko'rsatiladigan verifyClient callback funksiyasi ichida bajarilishi mumkin.

verifyClient birinchi argument sifatida info obyektini oladi; undan so'rov headerlarini olish uchun foydalanishingiz mumkin.

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: {
4    verifyClient: (info, next) => {
5      const authorization = info.req.headers?.authorization as string;
6      if (!authorization?.startsWith('Bearer ')) {
7        return next(false);
8      }
9      next(true);
10    },
11  }
12}),

Mercurius driver bilan subscriptionlarni yoqish

Subscriptionlarni yoqish uchun subscription xossasini true qiling.

TypeScript
1GraphQLModule.forRoot<MercuriusDriverConfig>({
2  driver: MercuriusDriver,
3  subscription: true,
4}),
Hint

Custom emitter sozlash, kiruvchi ulanishlarni validatsiya qilish va h.k. uchun opsiyalar obyektini ham uzatishingiz mumkin. Batafsil bu yerda o'qing (subscription bo'limi).