@biggora/claude-plugins 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/package.json +1 -1
- package/registry/registry.json +334 -244
- package/specs/coding.md +30 -0
- package/specs/pod.md +2 -0
- package/src/commands/skills/add.js +63 -7
- package/src/commands/skills/list.js +23 -52
- package/src/commands/skills/remove.js +26 -27
- package/src/commands/skills/resolve.js +155 -0
- package/src/commands/skills/update.js +58 -74
- package/src/skills/captcha/README.md +221 -0
- package/src/skills/captcha/SKILL.md +355 -0
- package/src/skills/captcha/references/captcha-types.md +254 -0
- package/src/skills/captcha/references/services.md +172 -0
- package/src/skills/captcha/references/stealth.md +238 -0
- package/src/skills/captcha/scripts/solve_captcha.py +323 -0
- package/src/skills/captcha/scripts/solve_image_grid.py +350 -0
- package/src/skills/google-merchant-api/SKILL.md +581 -0
- package/src/skills/google-merchant-api/references/accounts.md +247 -0
- package/src/skills/google-merchant-api/references/content-api-legacy.md +216 -0
- package/src/skills/google-merchant-api/references/datasources.md +233 -0
- package/src/skills/google-merchant-api/references/inventories.md +201 -0
- package/src/skills/google-merchant-api/references/migration.md +267 -0
- package/src/skills/google-merchant-api/references/products.md +316 -0
- package/src/skills/google-merchant-api/references/promotions.md +201 -0
- package/src/skills/google-merchant-api/references/reports.md +240 -0
- package/src/skills/lv-aggregators-api/SKILL.md +113 -0
- package/src/skills/lv-aggregators-api/references/integration-guide.md +368 -0
- package/src/skills/lv-aggregators-api/references/kurpirkt.md +103 -0
- package/src/skills/lv-aggregators-api/references/salidzini.md +122 -0
- package/src/skills/nest-best-practices/SKILL.md +251 -0
- package/src/skills/nest-best-practices/references/best-practices-request-lifecycle.md +158 -0
- package/src/skills/nest-best-practices/references/cli-monorepo.md +106 -0
- package/src/skills/nest-best-practices/references/cli-overview.md +157 -0
- package/src/skills/nest-best-practices/references/core-controllers.md +165 -0
- package/src/skills/nest-best-practices/references/core-dependency-injection.md +179 -0
- package/src/skills/nest-best-practices/references/core-middleware.md +139 -0
- package/src/skills/nest-best-practices/references/core-modules.md +138 -0
- package/src/skills/nest-best-practices/references/core-providers.md +188 -0
- package/src/skills/nest-best-practices/references/faq-raw-body-hybrid.md +122 -0
- package/src/skills/nest-best-practices/references/fundamentals-circular-dependency.md +89 -0
- package/src/skills/nest-best-practices/references/fundamentals-custom-decorators.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-dynamic-modules.md +125 -0
- package/src/skills/nest-best-practices/references/fundamentals-exception-filters.md +202 -0
- package/src/skills/nest-best-practices/references/fundamentals-execution-context.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-guards.md +136 -0
- package/src/skills/nest-best-practices/references/fundamentals-interceptors.md +187 -0
- package/src/skills/nest-best-practices/references/fundamentals-lazy-loading.md +89 -0
- package/src/skills/nest-best-practices/references/fundamentals-lifecycle-events.md +87 -0
- package/src/skills/nest-best-practices/references/fundamentals-module-reference.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-pipes.md +197 -0
- package/src/skills/nest-best-practices/references/fundamentals-provider-scopes.md +92 -0
- package/src/skills/nest-best-practices/references/fundamentals-testing.md +142 -0
- package/src/skills/nest-best-practices/references/graphql-overview.md +233 -0
- package/src/skills/nest-best-practices/references/graphql-resolvers-mutations.md +199 -0
- package/src/skills/nest-best-practices/references/graphql-scalars-unions-enums.md +180 -0
- package/src/skills/nest-best-practices/references/graphql-subscriptions.md +228 -0
- package/src/skills/nest-best-practices/references/microservices-grpc.md +175 -0
- package/src/skills/nest-best-practices/references/microservices-overview.md +221 -0
- package/src/skills/nest-best-practices/references/microservices-transports.md +119 -0
- package/src/skills/nest-best-practices/references/openapi-swagger.md +207 -0
- package/src/skills/nest-best-practices/references/recipes-authentication.md +97 -0
- package/src/skills/nest-best-practices/references/recipes-cqrs.md +176 -0
- package/src/skills/nest-best-practices/references/recipes-crud-generator.md +87 -0
- package/src/skills/nest-best-practices/references/recipes-documentation.md +93 -0
- package/src/skills/nest-best-practices/references/recipes-mongoose.md +153 -0
- package/src/skills/nest-best-practices/references/recipes-prisma.md +98 -0
- package/src/skills/nest-best-practices/references/recipes-terminus.md +148 -0
- package/src/skills/nest-best-practices/references/recipes-typeorm.md +122 -0
- package/src/skills/nest-best-practices/references/security-authorization.md +196 -0
- package/src/skills/nest-best-practices/references/security-cors-helmet-rate-limiting.md +204 -0
- package/src/skills/nest-best-practices/references/security-encryption-hashing.md +93 -0
- package/src/skills/nest-best-practices/references/techniques-caching.md +142 -0
- package/src/skills/nest-best-practices/references/techniques-compression-streaming-sse.md +194 -0
- package/src/skills/nest-best-practices/references/techniques-configuration.md +132 -0
- package/src/skills/nest-best-practices/references/techniques-database.md +153 -0
- package/src/skills/nest-best-practices/references/techniques-events.md +163 -0
- package/src/skills/nest-best-practices/references/techniques-fastify.md +137 -0
- package/src/skills/nest-best-practices/references/techniques-file-upload.md +140 -0
- package/src/skills/nest-best-practices/references/techniques-http-module.md +176 -0
- package/src/skills/nest-best-practices/references/techniques-logging.md +146 -0
- package/src/skills/nest-best-practices/references/techniques-mvc-serve-static.md +132 -0
- package/src/skills/nest-best-practices/references/techniques-queues.md +162 -0
- package/src/skills/nest-best-practices/references/techniques-serialization.md +158 -0
- package/src/skills/nest-best-practices/references/techniques-sessions-cookies.md +167 -0
- package/src/skills/nest-best-practices/references/techniques-task-scheduling.md +166 -0
- package/src/skills/nest-best-practices/references/techniques-validation.md +126 -0
- package/src/skills/nest-best-practices/references/techniques-versioning.md +153 -0
- package/src/skills/nest-best-practices/references/websockets-advanced.md +96 -0
- package/src/skills/nest-best-practices/references/websockets-gateways.md +215 -0
- package/src/skills/tailwindcss-best-practices/SKILL.md +180 -0
- package/src/skills/tailwindcss-best-practices/references/best-practices-utility-patterns.md +87 -0
- package/src/skills/tailwindcss-best-practices/references/core-installation.md +109 -0
- package/src/skills/tailwindcss-best-practices/references/core-preflight.md +200 -0
- package/src/skills/tailwindcss-best-practices/references/core-responsive.md +163 -0
- package/src/skills/tailwindcss-best-practices/references/core-source-detection.md +114 -0
- package/src/skills/tailwindcss-best-practices/references/core-theme.md +108 -0
- package/src/skills/tailwindcss-best-practices/references/core-utility-classes.md +59 -0
- package/src/skills/tailwindcss-best-practices/references/core-variants.md +204 -0
- package/src/skills/tailwindcss-best-practices/references/effects-form-controls.md +76 -0
- package/src/skills/tailwindcss-best-practices/references/effects-mask.md +91 -0
- package/src/skills/tailwindcss-best-practices/references/effects-scroll-snap.md +59 -0
- package/src/skills/tailwindcss-best-practices/references/effects-text-shadow.md +78 -0
- package/src/skills/tailwindcss-best-practices/references/effects-transition-animation.md +80 -0
- package/src/skills/tailwindcss-best-practices/references/effects-visibility-interactivity.md +82 -0
- package/src/skills/tailwindcss-best-practices/references/features-content-detection.md +175 -0
- package/src/skills/tailwindcss-best-practices/references/features-custom-styles.md +203 -0
- package/src/skills/tailwindcss-best-practices/references/features-dark-mode.md +137 -0
- package/src/skills/tailwindcss-best-practices/references/features-functions-directives.md +241 -0
- package/src/skills/tailwindcss-best-practices/references/features-upgrade.md +160 -0
- package/src/skills/tailwindcss-best-practices/references/layout-aspect-ratio.md +39 -0
- package/src/skills/tailwindcss-best-practices/references/layout-columns.md +80 -0
- package/src/skills/tailwindcss-best-practices/references/layout-display.md +110 -0
- package/src/skills/tailwindcss-best-practices/references/layout-flexbox.md +112 -0
- package/src/skills/tailwindcss-best-practices/references/layout-grid.md +87 -0
- package/src/skills/tailwindcss-best-practices/references/layout-height.md +97 -0
- package/src/skills/tailwindcss-best-practices/references/layout-inset.md +103 -0
- package/src/skills/tailwindcss-best-practices/references/layout-logical-properties.md +92 -0
- package/src/skills/tailwindcss-best-practices/references/layout-margin.md +126 -0
- package/src/skills/tailwindcss-best-practices/references/layout-min-max-sizing.md +63 -0
- package/src/skills/tailwindcss-best-practices/references/layout-object-fit-position.md +64 -0
- package/src/skills/tailwindcss-best-practices/references/layout-overflow.md +57 -0
- package/src/skills/tailwindcss-best-practices/references/layout-padding.md +77 -0
- package/src/skills/tailwindcss-best-practices/references/layout-position.md +85 -0
- package/src/skills/tailwindcss-best-practices/references/layout-tables.md +67 -0
- package/src/skills/tailwindcss-best-practices/references/layout-width.md +102 -0
- package/src/skills/tailwindcss-best-practices/references/transform-base.md +68 -0
- package/src/skills/tailwindcss-best-practices/references/transform-rotate.md +70 -0
- package/src/skills/tailwindcss-best-practices/references/transform-scale.md +83 -0
- package/src/skills/tailwindcss-best-practices/references/transform-skew.md +62 -0
- package/src/skills/tailwindcss-best-practices/references/transform-translate.md +77 -0
- package/src/skills/tailwindcss-best-practices/references/typography-font-text.md +142 -0
- package/src/skills/tailwindcss-best-practices/references/typography-list-style.md +65 -0
- package/src/skills/tailwindcss-best-practices/references/typography-text-align.md +60 -0
- package/src/skills/tailwindcss-best-practices/references/visual-background.md +76 -0
- package/src/skills/tailwindcss-best-practices/references/visual-border.md +108 -0
- package/src/skills/tailwindcss-best-practices/references/visual-effects.md +111 -0
- package/src/skills/tailwindcss-best-practices/references/visual-svg.md +82 -0
- package/src/skills/test-mobile-app/SKILL.md +11 -6
- package/src/skills/test-mobile-app/scripts/analyze_apk.py +15 -4
- package/src/skills/test-mobile-app/scripts/check_environment.py +5 -5
- package/src/skills/test-mobile-app/scripts/run_tests.py +1 -1
- package/src/skills/test-web-ui/SKILL.md +264 -84
- package/src/skills/test-web-ui/scripts/discover.py +25 -12
- package/src/skills/test-web-ui/scripts/run_tests.py +3 -2
- package/src/skills/typescript-expert/SKILL.md +145 -0
- package/src/skills/typescript-expert/commands/typescript-fix.md +65 -0
- package/src/skills/typescript-expert/references/advanced-conditional-types.md +190 -0
- package/src/skills/typescript-expert/references/advanced-decorators.md +243 -0
- package/src/skills/typescript-expert/references/advanced-mapped-types.md +223 -0
- package/src/skills/typescript-expert/references/advanced-template-literals.md +209 -0
- package/src/skills/typescript-expert/references/advanced-type-guards.md +308 -0
- package/src/skills/typescript-expert/references/best-practices-patterns.md +313 -0
- package/src/skills/typescript-expert/references/best-practices-performance.md +185 -0
- package/src/skills/typescript-expert/references/best-practices-tsconfig.md +242 -0
- package/src/skills/typescript-expert/references/core-generics.md +246 -0
- package/src/skills/typescript-expert/references/core-interfaces-types.md +231 -0
- package/src/skills/typescript-expert/references/core-type-system.md +261 -0
- package/src/skills/typescript-expert/references/core-utility-types.md +235 -0
- package/src/skills/typescript-expert/references/features-ts5x.md +370 -0
- package/src/skills/vite-best-practices/SKILL.md +115 -0
- package/src/skills/vite-best-practices/references/build-and-ssr.md +255 -0
- package/src/skills/vite-best-practices/references/core-config.md +231 -0
- package/src/skills/vite-best-practices/references/core-features.md +222 -0
- package/src/skills/vite-best-practices/references/core-plugin-api.md +294 -0
- package/src/skills/vite-best-practices/references/environment-api.md +108 -0
- package/src/skills/vite-best-practices/references/rolldown-migration.md +242 -0
- package/codex-cli-workspace/iteration-1/benchmark.json +0 -122
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/eval_metadata.json +0 -13
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/grading.json +0 -52
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/outputs/response.md +0 -163
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/timing.json +0 -5
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/grading.json +0 -58
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/outputs/response.md +0 -151
- package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/timing.json +0 -5
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/eval_metadata.json +0 -13
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/grading.json +0 -52
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/outputs/response.md +0 -86
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/timing.json +0 -5
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/grading.json +0 -58
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/outputs/response.md +0 -164
- package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/timing.json +0 -5
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/eval_metadata.json +0 -13
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/grading.json +0 -52
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/outputs/response.md +0 -130
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/timing.json +0 -5
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/grading.json +0 -64
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/outputs/response.md +0 -209
- package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/timing.json +0 -5
- package/codex-cli-workspace/iteration-1/review.html +0 -1325
- package/gemini-cli-workspace/iteration-1/benchmark.json +0 -86
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/eval_metadata.json +0 -37
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/outputs/response.md +0 -401
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/outputs/response.md +0 -405
- package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/eval_metadata.json +0 -37
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/outputs/response.md +0 -212
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/outputs/response.md +0 -427
- package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/eval_metadata.json +0 -32
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/grading.json +0 -32
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/outputs/response.md +0 -171
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/grading.json +0 -32
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/outputs/response.md +0 -199
- package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-1/review.html +0 -1325
- package/gemini-cli-workspace/iteration-2/benchmark.json +0 -173
- package/gemini-cli-workspace/iteration-2/benchmark.md +0 -28
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/eval_metadata.json +0 -37
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/outputs/response.md +0 -195
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/outputs/response.md +0 -377
- package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/eval_metadata.json +0 -37
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/outputs/response.md +0 -127
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/grading.json +0 -37
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/outputs/response.md +0 -164
- package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/eval_metadata.json +0 -32
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/grading.json +0 -32
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/outputs/response.md +0 -91
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/grading.json +0 -32
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/outputs/response.md +0 -112
- package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/timing.json +0 -5
- package/gemini-cli-workspace/iteration-2/eval-viewer.html +0 -1325
- package/screen-recording-workspace/evals.json +0 -41
- package/screen-recording-workspace/iteration-1/benchmark.json +0 -102
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/eval_metadata.json +0 -31
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/grading.json +0 -11
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/outputs/demo.mp4 +0 -0
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/timing.json +0 -5
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/grading.json +0 -11
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/outputs/demo.mp4 +0 -0
- package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/timing.json +0 -5
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/eval_metadata.json +0 -31
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/grading.json +0 -11
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/outputs/region_capture.mp4 +0 -0
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/timing.json +0 -5
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/grading.json +0 -11
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/outputs/region_capture.mp4 +0 -0
- package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/timing.json +0 -5
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/eval_metadata.json +0 -31
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/grading.json +0 -11
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/outputs/fallback_recording.mp4 +0 -0
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/timing.json +0 -5
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/grading.json +0 -11
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/outputs/fallback_recording.mp4 +0 -0
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/outputs/record_screen.py +0 -67
- package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/timing.json +0 -5
- package/screen-recording-workspace/iteration-1/review.html +0 -1325
- package/src/skills/codex-cli/evals/evals.json +0 -47
- package/src/skills/gemini-cli/evals/evals.json +0 -46
- package/src/skills/tm-search/evals/evals.json +0 -23
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql-subscriptions
|
|
3
|
+
description: Real-time GraphQL subscriptions with PubSub
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GraphQL Subscriptions
|
|
7
|
+
|
|
8
|
+
Subscriptions enable real-time server-to-client communication.
|
|
9
|
+
|
|
10
|
+
## Enable Subscriptions (Apollo)
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
14
|
+
driver: ApolloDriver,
|
|
15
|
+
subscriptions: {
|
|
16
|
+
'graphql-ws': true,
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Basic Subscription
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Resolver, Mutation, Subscription } from '@nestjs/graphql';
|
|
25
|
+
import { PubSub } from 'graphql-subscriptions';
|
|
26
|
+
|
|
27
|
+
const pubSub = new PubSub();
|
|
28
|
+
|
|
29
|
+
@Resolver(() => Comment)
|
|
30
|
+
export class CommentsResolver {
|
|
31
|
+
@Subscription(() => Comment)
|
|
32
|
+
commentAdded() {
|
|
33
|
+
return pubSub.asyncIterableIterator('commentAdded');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Mutation(() => Comment)
|
|
37
|
+
async addComment(@Args('input') input: CreateCommentInput) {
|
|
38
|
+
const comment = await this.commentsService.create(input);
|
|
39
|
+
|
|
40
|
+
// Publish event
|
|
41
|
+
pubSub.publish('commentAdded', { commentAdded: comment });
|
|
42
|
+
|
|
43
|
+
return comment;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Filtering Events
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
@Subscription(() => Comment, {
|
|
52
|
+
filter: (payload, variables) =>
|
|
53
|
+
payload.commentAdded.postId === variables.postId,
|
|
54
|
+
})
|
|
55
|
+
commentAdded(@Args('postId') postId: string) {
|
|
56
|
+
return pubSub.asyncIterableIterator('commentAdded');
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Transforming Payload
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
@Subscription(() => Comment, {
|
|
64
|
+
resolve: (payload) => payload.commentAdded,
|
|
65
|
+
})
|
|
66
|
+
commentAdded() {
|
|
67
|
+
return pubSub.asyncIterableIterator('commentAdded');
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Named Subscription
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
@Subscription(() => Comment, {
|
|
75
|
+
name: 'commentAdded',
|
|
76
|
+
})
|
|
77
|
+
subscribeToCommentAdded() {
|
|
78
|
+
return pubSub.asyncIterableIterator('commentAdded');
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## PubSub as Provider
|
|
83
|
+
|
|
84
|
+
Inject PubSub instead of global instance:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// pubsub.module.ts
|
|
88
|
+
@Module({
|
|
89
|
+
providers: [
|
|
90
|
+
{
|
|
91
|
+
provide: 'PUB_SUB',
|
|
92
|
+
useValue: new PubSub(),
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
exports: ['PUB_SUB'],
|
|
96
|
+
})
|
|
97
|
+
export class PubSubModule {}
|
|
98
|
+
|
|
99
|
+
// resolver
|
|
100
|
+
@Resolver()
|
|
101
|
+
export class CommentsResolver {
|
|
102
|
+
constructor(@Inject('PUB_SUB') private pubSub: PubSub) {}
|
|
103
|
+
|
|
104
|
+
@Subscription(() => Comment)
|
|
105
|
+
commentAdded() {
|
|
106
|
+
return this.pubSub.asyncIterableIterator('commentAdded');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Production PubSub
|
|
112
|
+
|
|
113
|
+
The default `PubSub` is not for production. Use Redis-backed implementation:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npm install graphql-redis-subscriptions ioredis
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { RedisPubSub } from 'graphql-redis-subscriptions';
|
|
121
|
+
import Redis from 'ioredis';
|
|
122
|
+
|
|
123
|
+
const options = {
|
|
124
|
+
host: 'localhost',
|
|
125
|
+
port: 6379,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
{
|
|
129
|
+
provide: 'PUB_SUB',
|
|
130
|
+
useFactory: () => new RedisPubSub({
|
|
131
|
+
publisher: new Redis(options),
|
|
132
|
+
subscriber: new Redis(options),
|
|
133
|
+
}),
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## WebSocket Authentication
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
141
|
+
driver: ApolloDriver,
|
|
142
|
+
subscriptions: {
|
|
143
|
+
'graphql-ws': {
|
|
144
|
+
onConnect: (context) => {
|
|
145
|
+
const { connectionParams, extra } = context;
|
|
146
|
+
const token = connectionParams.authToken;
|
|
147
|
+
|
|
148
|
+
if (!isValidToken(token)) {
|
|
149
|
+
throw new Error('Invalid token');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
extra.user = decodeToken(token);
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
context: ({ extra }) => ({
|
|
157
|
+
user: extra?.user,
|
|
158
|
+
}),
|
|
159
|
+
}),
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Mercurius Subscriptions
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
GraphQLModule.forRoot<MercuriusDriverConfig>({
|
|
166
|
+
driver: MercuriusDriver,
|
|
167
|
+
subscription: true,
|
|
168
|
+
}),
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
@Subscription(() => Comment)
|
|
173
|
+
commentAdded(@Context('pubsub') pubSub: PubSub) {
|
|
174
|
+
return pubSub.subscribe('commentAdded');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@Mutation(() => Comment)
|
|
178
|
+
async addComment(
|
|
179
|
+
@Args('input') input: CreateCommentInput,
|
|
180
|
+
@Context('pubsub') pubSub: PubSub,
|
|
181
|
+
) {
|
|
182
|
+
const comment = await this.commentsService.create(input);
|
|
183
|
+
|
|
184
|
+
await pubSub.publish({
|
|
185
|
+
topic: 'commentAdded',
|
|
186
|
+
payload: { commentAdded: comment },
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return comment;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Client-Side
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
import { createClient } from 'graphql-ws';
|
|
197
|
+
|
|
198
|
+
const client = createClient({
|
|
199
|
+
url: 'ws://localhost:3000/graphql',
|
|
200
|
+
connectionParams: {
|
|
201
|
+
authToken: 'Bearer xxx',
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
client.subscribe(
|
|
206
|
+
{
|
|
207
|
+
query: `subscription { commentAdded { id content } }`,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
next: (data) => console.log(data),
|
|
211
|
+
error: (error) => console.error(error),
|
|
212
|
+
complete: () => console.log('done'),
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Key Points
|
|
218
|
+
|
|
219
|
+
- Use `graphql-ws` instead of deprecated `subscriptions-transport-ws`
|
|
220
|
+
- Payload shape must match subscription return type
|
|
221
|
+
- Use Redis-backed PubSub for production (multi-instance support)
|
|
222
|
+
- `filter` function determines which clients receive events
|
|
223
|
+
- `resolve` function transforms the payload before sending
|
|
224
|
+
|
|
225
|
+
<!--
|
|
226
|
+
Source references:
|
|
227
|
+
- https://docs.nestjs.com/graphql/subscriptions
|
|
228
|
+
-->
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: microservices-grpc
|
|
3
|
+
description: gRPC microservices with Protocol Buffers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gRPC Microservices
|
|
7
|
+
|
|
8
|
+
High-performance RPC framework using Protocol Buffers.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i @grpc/grpc-js @grpc/proto-loader
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Server Setup
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
|
|
20
|
+
transport: Transport.GRPC,
|
|
21
|
+
options: {
|
|
22
|
+
package: 'hero',
|
|
23
|
+
protoPath: join(__dirname, 'hero/hero.proto'),
|
|
24
|
+
url: '0.0.0.0:50051',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Proto Definition
|
|
30
|
+
|
|
31
|
+
```protobuf
|
|
32
|
+
// hero.proto
|
|
33
|
+
syntax = "proto3";
|
|
34
|
+
|
|
35
|
+
package hero;
|
|
36
|
+
|
|
37
|
+
service HeroesService {
|
|
38
|
+
rpc FindOne (HeroById) returns (Hero) {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
message HeroById {
|
|
42
|
+
int32 id = 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
message Hero {
|
|
46
|
+
int32 id = 1;
|
|
47
|
+
string name = 2;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## nest-cli.json (Copy Proto Files)
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"compilerOptions": {
|
|
56
|
+
"assets": ["**/*.proto"],
|
|
57
|
+
"watchAssets": true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Controller
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
@Controller()
|
|
66
|
+
export class HeroesController {
|
|
67
|
+
@GrpcMethod('HeroesService', 'FindOne')
|
|
68
|
+
findOne(data: HeroById, metadata: Metadata, call: ServerUnaryCall<any, any>): Hero {
|
|
69
|
+
return this.items.find(({ id }) => id === data.id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Omit decorator args for auto-binding: `@GrpcMethod()` uses class/method name.
|
|
75
|
+
|
|
76
|
+
## Client
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
ClientsModule.register([
|
|
80
|
+
{
|
|
81
|
+
name: 'HERO_PACKAGE',
|
|
82
|
+
transport: Transport.GRPC,
|
|
83
|
+
options: {
|
|
84
|
+
package: 'hero',
|
|
85
|
+
protoPath: join(__dirname, 'hero/hero.proto'),
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
]),
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
@Injectable()
|
|
93
|
+
export class AppService implements OnModuleInit {
|
|
94
|
+
private heroesService: HeroesService;
|
|
95
|
+
|
|
96
|
+
constructor(@Inject('HERO_PACKAGE') private client: ClientGrpc) {}
|
|
97
|
+
|
|
98
|
+
onModuleInit() {
|
|
99
|
+
this.heroesService = this.client.getService<HeroesService>('HeroesService');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getHero() {
|
|
103
|
+
return this.heroesService.findOne({ id: 1 });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## gRPC Metadata
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Send
|
|
112
|
+
const metadata = new Metadata();
|
|
113
|
+
metadata.add('Set-Cookie', 'yummy_cookie=choco');
|
|
114
|
+
return this.heroesService.findOne({ id: 1 }, metadata);
|
|
115
|
+
|
|
116
|
+
// Receive and respond
|
|
117
|
+
findOne(data, metadata: Metadata, call: ServerUnaryCall<any, any>) {
|
|
118
|
+
const serverMetadata = new Metadata();
|
|
119
|
+
serverMetadata.add('Set-Cookie', 'choco');
|
|
120
|
+
call.sendMetadata(serverMetadata);
|
|
121
|
+
return hero;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Streaming
|
|
126
|
+
|
|
127
|
+
Use `@GrpcStreamMethod()` for duplex streams:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
@GrpcStreamMethod()
|
|
131
|
+
bidiHello(messages: Observable<any>, metadata, call): Observable<any> {
|
|
132
|
+
return messages.pipe(
|
|
133
|
+
map((msg) => ({ reply: `Hello, ${msg.greeting}!` })),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Or `@GrpcStreamCall()` for raw stream access:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
@GrpcStreamCall()
|
|
142
|
+
bidiHello(requestStream: any) {
|
|
143
|
+
requestStream.on('data', (msg) => {
|
|
144
|
+
requestStream.write({ reply: 'Hello!' });
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## gRPC Reflection
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm i @grpc/reflection
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { ReflectionService } from '@grpc/reflection';
|
|
157
|
+
|
|
158
|
+
options: {
|
|
159
|
+
onLoadPackageDefinition: (pkg, server) => {
|
|
160
|
+
new ReflectionService(pkg).addToServer(server);
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Key Points
|
|
166
|
+
|
|
167
|
+
- Use `ClientGrpc.getService()` not `ClientProxy`
|
|
168
|
+
- Methods are lowerCamelCase in TypeScript
|
|
169
|
+
- Set `loader.keepCase: true` for underscore field names
|
|
170
|
+
- Streaming handlers must return `Observable` for full-duplex
|
|
171
|
+
|
|
172
|
+
<!--
|
|
173
|
+
Source references:
|
|
174
|
+
- https://docs.nestjs.com/microservices/grpc
|
|
175
|
+
-->
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: microservices
|
|
3
|
+
description: Building microservices with various transport layers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Microservices
|
|
7
|
+
|
|
8
|
+
NestJS supports microservice architecture with multiple transport protocols.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @nestjs/microservices
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Available Transporters
|
|
17
|
+
|
|
18
|
+
| Transport | Package |
|
|
19
|
+
|-----------|---------|
|
|
20
|
+
| TCP | Built-in |
|
|
21
|
+
| Redis | `redis` |
|
|
22
|
+
| MQTT | `mqtt` |
|
|
23
|
+
| NATS | `nats` |
|
|
24
|
+
| RabbitMQ | `amqplib` |
|
|
25
|
+
| Kafka | `kafkajs` |
|
|
26
|
+
| gRPC | `@grpc/grpc-js` |
|
|
27
|
+
|
|
28
|
+
## Creating a Microservice
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// main.ts
|
|
32
|
+
import { NestFactory } from '@nestjs/core';
|
|
33
|
+
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
|
|
34
|
+
import { AppModule } from './app.module';
|
|
35
|
+
|
|
36
|
+
async function bootstrap() {
|
|
37
|
+
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
|
|
38
|
+
AppModule,
|
|
39
|
+
{
|
|
40
|
+
transport: Transport.TCP,
|
|
41
|
+
options: { host: 'localhost', port: 3001 },
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
await app.listen();
|
|
45
|
+
}
|
|
46
|
+
bootstrap();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Hybrid Application
|
|
50
|
+
|
|
51
|
+
Combine HTTP and microservice:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const app = await NestFactory.create(AppModule);
|
|
55
|
+
app.connectMicroservice<MicroserviceOptions>({
|
|
56
|
+
transport: Transport.TCP,
|
|
57
|
+
options: { port: 3001 },
|
|
58
|
+
});
|
|
59
|
+
await app.startAllMicroservices();
|
|
60
|
+
await app.listen(3000);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Request-Response Pattern
|
|
64
|
+
|
|
65
|
+
### Handler (Microservice)
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { Controller } from '@nestjs/common';
|
|
69
|
+
import { MessagePattern, Payload } from '@nestjs/microservices';
|
|
70
|
+
|
|
71
|
+
@Controller()
|
|
72
|
+
export class MathController {
|
|
73
|
+
@MessagePattern({ cmd: 'sum' })
|
|
74
|
+
sum(@Payload() data: number[]): number {
|
|
75
|
+
return data.reduce((a, b) => a + b, 0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Client (Producer)
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { Module } from '@nestjs/common';
|
|
84
|
+
import { ClientsModule, Transport } from '@nestjs/microservices';
|
|
85
|
+
|
|
86
|
+
@Module({
|
|
87
|
+
imports: [
|
|
88
|
+
ClientsModule.register([
|
|
89
|
+
{
|
|
90
|
+
name: 'MATH_SERVICE',
|
|
91
|
+
transport: Transport.TCP,
|
|
92
|
+
options: { host: 'localhost', port: 3001 },
|
|
93
|
+
},
|
|
94
|
+
]),
|
|
95
|
+
],
|
|
96
|
+
})
|
|
97
|
+
export class AppModule {}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
102
|
+
import { ClientProxy } from '@nestjs/microservices';
|
|
103
|
+
import { firstValueFrom } from 'rxjs';
|
|
104
|
+
|
|
105
|
+
@Injectable()
|
|
106
|
+
export class AppService {
|
|
107
|
+
constructor(@Inject('MATH_SERVICE') private client: ClientProxy) {}
|
|
108
|
+
|
|
109
|
+
async calculate(): Promise<number> {
|
|
110
|
+
const pattern = { cmd: 'sum' };
|
|
111
|
+
const payload = [1, 2, 3, 4, 5];
|
|
112
|
+
return firstValueFrom(this.client.send<number>(pattern, payload));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Event-Based Pattern
|
|
118
|
+
|
|
119
|
+
### Handler
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
@EventPattern('user_created')
|
|
123
|
+
async handleUserCreated(@Payload() data: CreateUserEvent) {
|
|
124
|
+
await this.usersService.processNewUser(data);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Emitter
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
async createUser(dto: CreateUserDto) {
|
|
132
|
+
const user = await this.usersRepository.create(dto);
|
|
133
|
+
this.client.emit('user_created', new CreateUserEvent(user));
|
|
134
|
+
return user;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Accessing Context
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { Ctx, Payload, NatsContext } from '@nestjs/microservices';
|
|
142
|
+
|
|
143
|
+
@MessagePattern('time.us.*')
|
|
144
|
+
getDate(@Payload() data: any, @Ctx() context: NatsContext) {
|
|
145
|
+
console.log(`Subject: ${context.getSubject()}`);
|
|
146
|
+
return new Date().toISOString();
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Async Configuration
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
ClientsModule.registerAsync([
|
|
154
|
+
{
|
|
155
|
+
name: 'MATH_SERVICE',
|
|
156
|
+
imports: [ConfigModule],
|
|
157
|
+
useFactory: async (configService: ConfigService) => ({
|
|
158
|
+
transport: Transport.TCP,
|
|
159
|
+
options: {
|
|
160
|
+
host: configService.get('MATH_HOST'),
|
|
161
|
+
port: configService.get('MATH_PORT'),
|
|
162
|
+
},
|
|
163
|
+
}),
|
|
164
|
+
inject: [ConfigService],
|
|
165
|
+
},
|
|
166
|
+
]);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Timeout Handling
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { timeout } from 'rxjs/operators';
|
|
173
|
+
|
|
174
|
+
this.client
|
|
175
|
+
.send<number>({ cmd: 'sum' }, [1, 2, 3])
|
|
176
|
+
.pipe(timeout(5000));
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## TLS Support
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// Server
|
|
183
|
+
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
|
|
184
|
+
AppModule,
|
|
185
|
+
{
|
|
186
|
+
transport: Transport.TCP,
|
|
187
|
+
options: {
|
|
188
|
+
tlsOptions: {
|
|
189
|
+
key: fs.readFileSync('key.pem'),
|
|
190
|
+
cert: fs.readFileSync('cert.pem'),
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Client
|
|
197
|
+
ClientsModule.register([
|
|
198
|
+
{
|
|
199
|
+
name: 'SERVICE',
|
|
200
|
+
transport: Transport.TCP,
|
|
201
|
+
options: {
|
|
202
|
+
tlsOptions: {
|
|
203
|
+
ca: [fs.readFileSync('ca.pem')],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Key Points
|
|
211
|
+
|
|
212
|
+
- `@MessagePattern()` for request-response (requires reply)
|
|
213
|
+
- `@EventPattern()` for fire-and-forget events
|
|
214
|
+
- Client connection is lazy (connects on first call)
|
|
215
|
+
- Use `firstValueFrom` to convert Observable to Promise
|
|
216
|
+
- TCP is the default transport
|
|
217
|
+
|
|
218
|
+
<!--
|
|
219
|
+
Source references:
|
|
220
|
+
- https://docs.nestjs.com/microservices/basics
|
|
221
|
+
-->
|