@ngxtm/devkit 3.4.0 → 3.4.1
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/package.json +2 -1
- package/rules/README.md +141 -0
- package/rules/dart/best-practices/SKILL.md +23 -0
- package/rules/dart/language/SKILL.md +52 -0
- package/rules/dart/tooling/SKILL.md +43 -0
- package/rules/dotnet/aspnet-core/SKILL.md +92 -0
- package/rules/dotnet/aspnet-core/references/REFERENCE.md +335 -0
- package/rules/dotnet/best-practices/SKILL.md +101 -0
- package/rules/dotnet/best-practices/references/REFERENCE.md +256 -0
- package/rules/dotnet/blazor/SKILL.md +146 -0
- package/rules/dotnet/blazor/references/REFERENCE.md +392 -0
- package/rules/dotnet/language/SKILL.md +82 -0
- package/rules/dotnet/language/references/REFERENCE.md +222 -0
- package/rules/dotnet/patterns.rule.md +388 -0
- package/rules/dotnet/razor-pages/SKILL.md +124 -0
- package/rules/dotnet/razor-pages/references/REFERENCE.md +321 -0
- package/rules/dotnet/security/SKILL.md +89 -0
- package/rules/dotnet/security/references/REFERENCE.md +295 -0
- package/rules/dotnet/tooling/SKILL.md +92 -0
- package/rules/dotnet/tooling/references/REFERENCE.md +300 -0
- package/rules/flutter/auto-route-navigation/SKILL.md +43 -0
- package/rules/flutter/auto-route-navigation/references/REFERENCE.md +19 -0
- package/rules/flutter/auto-route-navigation/references/router-config.md +62 -0
- package/rules/flutter/bloc-state-management/SKILL.md +64 -0
- package/rules/flutter/bloc-state-management/references/REFERENCE.md +20 -0
- package/rules/flutter/bloc-state-management/references/auth-bloc-example.md +52 -0
- package/rules/flutter/bloc-state-management/references/equatable-usage.md +56 -0
- package/rules/flutter/bloc-state-management/references/property-based-state.md +68 -0
- package/rules/flutter/bloc.rule.md +76 -0
- package/rules/flutter/cicd/SKILL.md +48 -0
- package/rules/flutter/cicd/references/advanced-workflow.md +66 -0
- package/rules/flutter/cicd/references/fastlane.md +139 -0
- package/rules/flutter/cicd/references/github-actions.md +59 -0
- package/rules/flutter/dependency-injection/SKILL.md +42 -0
- package/rules/flutter/dependency-injection/references/REFERENCE.md +15 -0
- package/rules/flutter/dependency-injection/references/modules.md +37 -0
- package/rules/flutter/error-handling/SKILL.md +32 -0
- package/rules/flutter/error-handling/references/REFERENCE.md +19 -0
- package/rules/flutter/error-handling/references/error-mapping.md +31 -0
- package/rules/flutter/feature-based-clean-architecture/SKILL.md +46 -0
- package/rules/flutter/feature-based-clean-architecture/references/REFERENCE.md +14 -0
- package/rules/flutter/feature-based-clean-architecture/references/folder-structure.md +36 -0
- package/rules/flutter/getx-navigation/SKILL.md +70 -0
- package/rules/flutter/getx-navigation/references/app-pages.md +40 -0
- package/rules/flutter/getx-navigation/references/middleware-example.md +29 -0
- package/rules/flutter/getx-state-management/SKILL.md +76 -0
- package/rules/flutter/getx-state-management/references/binding-example.md +32 -0
- package/rules/flutter/getx-state-management/references/reactive-vs-simple.md +39 -0
- package/rules/flutter/go-router-navigation/SKILL.md +57 -0
- package/rules/flutter/idiomatic-flutter/SKILL.md +20 -0
- package/rules/flutter/layer-based-clean-architecture/SKILL.md +50 -0
- package/rules/flutter/layer-based-clean-architecture/references/REFERENCE.md +60 -0
- package/rules/flutter/layer-based-clean-architecture/references/repository-mapping.md +50 -0
- package/rules/flutter/localization/SKILL.md +50 -0
- package/rules/flutter/localization/references/REFERENCE.md +48 -0
- package/rules/flutter/localization/references/sheet-loader.md +33 -0
- package/rules/flutter/navigator-v1-navigation/SKILL.md +71 -0
- package/rules/flutter/navigator-v1-navigation/references/on-generate-route.md +48 -0
- package/rules/flutter/performance/SKILL.md +24 -0
- package/rules/flutter/retrofit-networking/SKILL.md +51 -0
- package/rules/flutter/retrofit-networking/references/REFERENCE.md +19 -0
- package/rules/flutter/retrofit-networking/references/token-refresh.md +40 -0
- package/rules/flutter/riverpod-state-management/SKILL.md +53 -0
- package/rules/flutter/riverpod-state-management/references/architecture.md +124 -0
- package/rules/flutter/riverpod-state-management/references/best-practices.md +89 -0
- package/rules/flutter/riverpod-state-management/references/testing.md +73 -0
- package/rules/flutter/riverpod.rule.md +78 -0
- package/rules/flutter/security/SKILL.md +33 -0
- package/rules/flutter/security/references/REFERENCE.md +15 -0
- package/rules/flutter/security/references/network-security.md +28 -0
- package/rules/flutter/testing/SKILL.md +44 -0
- package/rules/flutter/testing/references/REFERENCE.md +21 -0
- package/rules/flutter/testing/references/bloc-testing.md +38 -0
- package/rules/flutter/testing/references/integration-testing.md +128 -0
- package/rules/flutter/testing/references/robot-pattern.md +82 -0
- package/rules/flutter/testing/references/unit-testing.md +130 -0
- package/rules/flutter/testing/references/widget-testing.md +120 -0
- package/rules/flutter/widgets/SKILL.md +37 -0
- package/rules/golang/chi-router/SKILL.md +219 -0
- package/rules/golang/chi-router/references/REFERENCE.md +13 -0
- package/rules/golang/chi-router/references/routing-patterns.md +205 -0
- package/rules/golang/cobra-cli/SKILL.md +227 -0
- package/rules/golang/cobra-cli/references/REFERENCE.md +13 -0
- package/rules/golang/cobra-cli/references/command-patterns.md +224 -0
- package/rules/golang/core/SKILL.md +210 -0
- package/rules/golang/core/references/REFERENCE.md +14 -0
- package/rules/golang/core/references/concurrency-patterns.md +114 -0
- package/rules/golang/core/references/error-handling.md +87 -0
- package/rules/golang/echo-framework/SKILL.md +215 -0
- package/rules/golang/echo-framework/references/REFERENCE.md +14 -0
- package/rules/golang/echo-framework/references/middleware-patterns.md +141 -0
- package/rules/golang/echo-framework/references/routing-patterns.md +140 -0
- package/rules/golang/ent-orm/SKILL.md +239 -0
- package/rules/golang/ent-orm/references/REFERENCE.md +13 -0
- package/rules/golang/ent-orm/references/schema-patterns.md +255 -0
- package/rules/golang/fiber-framework/SKILL.md +196 -0
- package/rules/golang/fiber-framework/references/REFERENCE.md +13 -0
- package/rules/golang/fiber-framework/references/routing-patterns.md +191 -0
- package/rules/golang/gin-framework/SKILL.md +205 -0
- package/rules/golang/gin-framework/references/REFERENCE.md +14 -0
- package/rules/golang/gin-framework/references/middleware-patterns.md +119 -0
- package/rules/golang/gorm-orm/SKILL.md +196 -0
- package/rules/golang/gorm-orm/references/REFERENCE.md +14 -0
- package/rules/golang/gorm-orm/references/model-definitions.md +167 -0
- package/rules/golang/gorm-orm/references/query-patterns.md +161 -0
- package/rules/golang/grpc/SKILL.md +231 -0
- package/rules/golang/grpc/references/REFERENCE.md +13 -0
- package/rules/golang/grpc/references/service-patterns.md +276 -0
- package/rules/golang/testify/SKILL.md +239 -0
- package/rules/golang/testify/references/REFERENCE.md +13 -0
- package/rules/golang/testify/references/assert-patterns.md +170 -0
- package/rules/golang/validator/SKILL.md +234 -0
- package/rules/golang/validator/references/REFERENCE.md +13 -0
- package/rules/golang/validator/references/validation-tags.md +211 -0
- package/rules/golang/viper-config/SKILL.md +244 -0
- package/rules/golang/viper-config/references/REFERENCE.md +13 -0
- package/rules/golang/viper-config/references/config-loading.md +181 -0
- package/rules/golang/wire-di/SKILL.md +243 -0
- package/rules/golang/wire-di/references/REFERENCE.md +13 -0
- package/rules/golang/wire-di/references/provider-patterns.md +193 -0
- package/rules/golang/zap-logging/SKILL.md +203 -0
- package/rules/golang/zap-logging/references/REFERENCE.md +13 -0
- package/rules/golang/zap-logging/references/logger-config.md +165 -0
- package/rules/java/build-gradle/SKILL.md +92 -0
- package/rules/java/build-gradle/references/REFERENCE.md +14 -0
- package/rules/java/build-gradle/references/kotlin-dsl.md +118 -0
- package/rules/java/build-gradle/references/task-configuration.md +132 -0
- package/rules/java/build-maven/SKILL.md +86 -0
- package/rules/java/build-maven/references/REFERENCE.md +14 -0
- package/rules/java/build-maven/references/dependency-management.md +111 -0
- package/rules/java/build-maven/references/lifecycle-phases.md +114 -0
- package/rules/java/graalvm-native/SKILL.md +105 -0
- package/rules/java/graalvm-native/references/REFERENCE.md +12 -0
- package/rules/java/java-collections-streams/SKILL.md +148 -0
- package/rules/java/java-collections-streams/references/REFERENCE.md +15 -0
- package/rules/java/java-collections-streams/references/collectors-patterns.md +178 -0
- package/rules/java/java-collections-streams/references/stream-pipelines.md +165 -0
- package/rules/java/java-concurrency/SKILL.md +187 -0
- package/rules/java/java-concurrency/references/REFERENCE.md +17 -0
- package/rules/java/java-concurrency/references/completable-future.md +165 -0
- package/rules/java/java-concurrency/references/executor-patterns.md +176 -0
- package/rules/java/java-concurrency/references/virtual-threads.md +190 -0
- package/rules/java/java-core-language/SKILL.md +121 -0
- package/rules/java/java-core-language/references/REFERENCE.md +15 -0
- package/rules/java/java-core-language/references/jvm-memory-model.md +160 -0
- package/rules/java/java-core-language/references/modern-java-features.md +168 -0
- package/rules/java/java-project-structure/SKILL.md +195 -0
- package/rules/java/java-project-structure/references/REFERENCE.md +15 -0
- package/rules/java/java-project-structure/references/maven-project-layout.md +199 -0
- package/rules/java/java-project-structure/references/module-system.md +159 -0
- package/rules/java/micronaut-core/SKILL.md +99 -0
- package/rules/java/micronaut-core/references/REFERENCE.md +12 -0
- package/rules/java/micronaut-reactive/SKILL.md +68 -0
- package/rules/java/micronaut-reactive/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-core/SKILL.md +85 -0
- package/rules/java/quarkus-core/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-reactive/SKILL.md +67 -0
- package/rules/java/quarkus-reactive/references/REFERENCE.md +12 -0
- package/rules/java/spring-batch/SKILL.md +102 -0
- package/rules/java/spring-batch/references/REFERENCE.md +12 -0
- package/rules/java/spring-boot-architecture/SKILL.md +206 -0
- package/rules/java/spring-boot-architecture/references/REFERENCE.md +15 -0
- package/rules/java/spring-boot-architecture/references/auto-configuration.md +158 -0
- package/rules/java/spring-boot-architecture/references/configuration-properties.md +202 -0
- package/rules/java/spring-boot-web/SKILL.md +217 -0
- package/rules/java/spring-boot-web/references/REFERENCE.md +17 -0
- package/rules/java/spring-cloud/SKILL.md +109 -0
- package/rules/java/spring-cloud/references/REFERENCE.md +13 -0
- package/rules/java/spring-data-jpa/SKILL.md +241 -0
- package/rules/java/spring-data-jpa/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/SKILL.md +161 -0
- package/rules/java/spring-security/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/references/jwt-auth-flow.md +213 -0
- package/rules/java/testing-junit-mockito/SKILL.md +135 -0
- package/rules/java/testing-junit-mockito/references/REFERENCE.md +15 -0
- package/rules/java/testing-junit-mockito/references/junit5-patterns.md +159 -0
- package/rules/java/testing-junit-mockito/references/mockito-patterns.md +148 -0
- package/rules/java/testing-junit-mockito/references/spring-boot-testing.md +152 -0
- package/rules/javascript/best-practices/SKILL.md +64 -0
- package/rules/javascript/best-practices/references/REFERENCE.md +91 -0
- package/rules/javascript/language/SKILL.md +71 -0
- package/rules/javascript/language/references/REFERENCE.md +106 -0
- package/rules/javascript/tooling/SKILL.md +60 -0
- package/rules/javascript/tooling/references/REFERENCE.md +107 -0
- package/rules/metadata.json +54 -0
- package/rules/nestjs/api-standards/SKILL.md +47 -0
- package/rules/nestjs/api-standards/references/pagination-wrapper.md +87 -0
- package/rules/nestjs/architecture/SKILL.md +68 -0
- package/rules/nestjs/architecture/references/dynamic-module.md +53 -0
- package/rules/nestjs/caching/SKILL.md +51 -0
- package/rules/nestjs/caching/references/REFERENCE.md +13 -0
- package/rules/nestjs/caching/references/cache-patterns.md +183 -0
- package/rules/nestjs/configuration/SKILL.md +41 -0
- package/rules/nestjs/configuration/references/REFERENCE.md +13 -0
- package/rules/nestjs/configuration/references/config-patterns.md +184 -0
- package/rules/nestjs/controllers-services/SKILL.md +63 -0
- package/rules/nestjs/controllers-services/references/REFERENCE.md +14 -0
- package/rules/nestjs/controllers-services/references/controller-patterns.md +119 -0
- package/rules/nestjs/controllers-services/references/service-patterns.md +129 -0
- package/rules/nestjs/database/SKILL.md +102 -0
- package/rules/nestjs/database/references/REFERENCE.md +14 -0
- package/rules/nestjs/database/references/typeorm-patterns.md +156 -0
- package/rules/nestjs/deployment/SKILL.md +36 -0
- package/rules/nestjs/deployment/references/REFERENCE.md +13 -0
- package/rules/nestjs/deployment/references/deployment-patterns.md +140 -0
- package/rules/nestjs/documentation/SKILL.md +64 -0
- package/rules/nestjs/documentation/references/REFERENCE.md +13 -0
- package/rules/nestjs/documentation/references/swagger-patterns.md +139 -0
- package/rules/nestjs/error-handling/SKILL.md +55 -0
- package/rules/nestjs/error-handling/references/REFERENCE.md +13 -0
- package/rules/nestjs/error-handling/references/exception-filters.md +152 -0
- package/rules/nestjs/file-uploads/SKILL.md +35 -0
- package/rules/nestjs/file-uploads/references/REFERENCE.md +13 -0
- package/rules/nestjs/file-uploads/references/upload-patterns.md +125 -0
- package/rules/nestjs/observability/SKILL.md +39 -0
- package/rules/nestjs/observability/references/REFERENCE.md +13 -0
- package/rules/nestjs/observability/references/logging-metrics.md +175 -0
- package/rules/nestjs/performance/SKILL.md +60 -0
- package/rules/nestjs/performance/references/REFERENCE.md +13 -0
- package/rules/nestjs/performance/references/performance-patterns.md +107 -0
- package/rules/nestjs/real-time/SKILL.md +45 -0
- package/rules/nestjs/real-time/references/REFERENCE.md +13 -0
- package/rules/nestjs/real-time/references/websocket-patterns.md +121 -0
- package/rules/nestjs/scheduling/SKILL.md +39 -0
- package/rules/nestjs/scheduling/references/REFERENCE.md +13 -0
- package/rules/nestjs/scheduling/references/scheduling-patterns.md +137 -0
- package/rules/nestjs/search/SKILL.md +41 -0
- package/rules/nestjs/search/references/REFERENCE.md +13 -0
- package/rules/nestjs/search/references/search-patterns.md +137 -0
- package/rules/nestjs/security/SKILL.md +87 -0
- package/rules/nestjs/security/references/REFERENCE.md +14 -0
- package/rules/nestjs/security/references/authentication.md +151 -0
- package/rules/nestjs/testing/SKILL.md +40 -0
- package/rules/nestjs/testing/references/REFERENCE.md +14 -0
- package/rules/nestjs/testing/references/unit-testing.md +179 -0
- package/rules/nestjs/transport/SKILL.md +45 -0
- package/rules/nestjs/transport/references/REFERENCE.md +13 -0
- package/rules/nestjs/transport/references/microservices-patterns.md +170 -0
- package/rules/nextjs/app-router/SKILL.md +46 -0
- package/rules/nextjs/app-router/references/REFERENCE.md +14 -0
- package/rules/nextjs/app-router/references/routing-patterns.md +182 -0
- package/rules/nextjs/architecture/SKILL.md +44 -0
- package/rules/nextjs/architecture/references/fsd-structure.md +77 -0
- package/rules/nextjs/authentication/SKILL.md +29 -0
- package/rules/nextjs/authentication/references/auth-implementation.md +73 -0
- package/rules/nextjs/caching/SKILL.md +66 -0
- package/rules/nextjs/caching/references/REFERENCE.md +13 -0
- package/rules/nextjs/caching/references/cache-strategies.md +168 -0
- package/rules/nextjs/data-access-layer/SKILL.md +33 -0
- package/rules/nextjs/data-access-layer/references/patterns.md +66 -0
- package/rules/nextjs/data-fetching/SKILL.md +59 -0
- package/rules/nextjs/data-fetching/references/REFERENCE.md +13 -0
- package/rules/nextjs/data-fetching/references/fetch-patterns.md +160 -0
- package/rules/nextjs/internationalization/SKILL.md +105 -0
- package/rules/nextjs/internationalization/references/REFERENCE.md +13 -0
- package/rules/nextjs/internationalization/references/i18n-patterns.md +180 -0
- package/rules/nextjs/optimization/SKILL.md +64 -0
- package/rules/nextjs/optimization/references/REFERENCE.md +13 -0
- package/rules/nextjs/optimization/references/optimization-patterns.md +190 -0
- package/rules/nextjs/rendering/SKILL.md +91 -0
- package/rules/nextjs/rendering/references/REFERENCE.md +13 -0
- package/rules/nextjs/rendering/references/rendering-modes.md +163 -0
- package/rules/nextjs/server-actions/SKILL.md +46 -0
- package/rules/nextjs/server-actions/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-actions/references/action-patterns.md +188 -0
- package/rules/nextjs/server-components/SKILL.md +52 -0
- package/rules/nextjs/server-components/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-components/references/component-patterns.md +175 -0
- package/rules/nextjs/state-management/SKILL.md +73 -0
- package/rules/nextjs/state-management/references/REFERENCE.md +13 -0
- package/rules/nextjs/state-management/references/state-patterns.md +218 -0
- package/rules/nextjs/styling/SKILL.md +31 -0
- package/rules/nextjs/styling/references/implementation.md +56 -0
- package/rules/react/component-patterns/SKILL.md +66 -0
- package/rules/react/component-patterns/references/REFERENCE.md +126 -0
- package/rules/react/hooks/SKILL.md +60 -0
- package/rules/react/hooks/references/REFERENCE.md +132 -0
- package/rules/react/hooks.rule.md +79 -0
- package/rules/react/performance/SKILL.md +69 -0
- package/rules/react/performance/references/REFERENCE.md +143 -0
- package/rules/react/security/SKILL.md +46 -0
- package/rules/react/security/references/REFERENCE.md +170 -0
- package/rules/react/state-management/SKILL.md +56 -0
- package/rules/react/state-management/references/REFERENCE.md +137 -0
- package/rules/react/testing/SKILL.md +45 -0
- package/rules/react/testing/references/REFERENCE.md +149 -0
- package/rules/react/tooling/SKILL.md +39 -0
- package/rules/react/typescript/SKILL.md +53 -0
- package/rules/rust/actix-web/SKILL.md +160 -0
- package/rules/rust/actix-web/references/REFERENCE.md +13 -0
- package/rules/rust/actix-web/references/handler-patterns.md +198 -0
- package/rules/rust/async-graphql/SKILL.md +228 -0
- package/rules/rust/async-graphql/references/REFERENCE.md +13 -0
- package/rules/rust/async-graphql/references/schema-patterns.md +215 -0
- package/rules/rust/axum/SKILL.md +161 -0
- package/rules/rust/axum/references/REFERENCE.md +14 -0
- package/rules/rust/axum/references/handler-patterns.md +97 -0
- package/rules/rust/bevy/SKILL.md +206 -0
- package/rules/rust/bevy/references/REFERENCE.md +13 -0
- package/rules/rust/bevy/references/ecs-patterns.md +226 -0
- package/rules/rust/clap/SKILL.md +217 -0
- package/rules/rust/clap/references/REFERENCE.md +13 -0
- package/rules/rust/clap/references/derive-patterns.md +205 -0
- package/rules/rust/core/SKILL.md +154 -0
- package/rules/rust/core/references/REFERENCE.md +14 -0
- package/rules/rust/core/references/error-handling.md +92 -0
- package/rules/rust/diesel-orm/SKILL.md +176 -0
- package/rules/rust/diesel-orm/references/REFERENCE.md +13 -0
- package/rules/rust/diesel-orm/references/schema-patterns.md +206 -0
- package/rules/rust/rocket/SKILL.md +182 -0
- package/rules/rust/rocket/references/REFERENCE.md +13 -0
- package/rules/rust/rocket/references/handler-patterns.md +209 -0
- package/rules/rust/sea-orm/SKILL.md +230 -0
- package/rules/rust/sea-orm/references/REFERENCE.md +13 -0
- package/rules/rust/sea-orm/references/entity-patterns.md +221 -0
- package/rules/rust/serde-serialization/SKILL.md +150 -0
- package/rules/rust/serde-serialization/references/REFERENCE.md +13 -0
- package/rules/rust/serde-serialization/references/serialization-patterns.md +199 -0
- package/rules/rust/sqlx-database/SKILL.md +140 -0
- package/rules/rust/sqlx-database/references/REFERENCE.md +13 -0
- package/rules/rust/sqlx-database/references/query-patterns.md +210 -0
- package/rules/rust/tauri/SKILL.md +180 -0
- package/rules/rust/tauri/references/REFERENCE.md +13 -0
- package/rules/rust/tauri/references/command-patterns.md +209 -0
- package/rules/rust/tokio-runtime/SKILL.md +167 -0
- package/rules/rust/tokio-runtime/references/REFERENCE.md +14 -0
- package/rules/rust/tokio-runtime/references/async-patterns.md +137 -0
- package/rules/rust/tokio-runtime/references/synchronization.md +152 -0
- package/rules/rust/tonic/SKILL.md +231 -0
- package/rules/rust/tonic/references/REFERENCE.md +13 -0
- package/rules/rust/tonic/references/service-patterns.md +213 -0
- package/rules/rust/tracing/SKILL.md +214 -0
- package/rules/rust/tracing/references/REFERENCE.md +13 -0
- package/rules/rust/tracing/references/instrumentation.md +187 -0
- package/rules/typescript/best-practices/SKILL.md +108 -0
- package/rules/typescript/best-practices/references/REFERENCE.md +68 -0
- package/rules/typescript/language/SKILL.md +72 -0
- package/rules/typescript/language/references/REFERENCE.md +67 -0
- package/rules/typescript/patterns.rule.md +85 -0
- package/rules/typescript/security/SKILL.md +59 -0
- package/rules/typescript/security/references/REFERENCE.md +113 -0
- package/rules/typescript/tooling/SKILL.md +52 -0
- package/rules/typescript/tooling/references/REFERENCE.md +110 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# NestJS Scheduling Patterns
|
|
2
|
+
|
|
3
|
+
## Cron Jobs
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Injectable } from '@nestjs/common';
|
|
7
|
+
import { Cron, CronExpression, SchedulerRegistry } from '@nestjs/schedule';
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class TasksService {
|
|
11
|
+
constructor(private schedulerRegistry: SchedulerRegistry) {}
|
|
12
|
+
|
|
13
|
+
// Every day at midnight
|
|
14
|
+
@Cron('0 0 * * *')
|
|
15
|
+
handleDailyCleanup() {
|
|
16
|
+
console.log('Running daily cleanup');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Every 5 minutes
|
|
20
|
+
@Cron(CronExpression.EVERY_5_MINUTES)
|
|
21
|
+
handleFrequentTask() {
|
|
22
|
+
console.log('Running every 5 minutes');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Dynamic cron job
|
|
26
|
+
addCronJob(name: string, cronTime: string, callback: () => void) {
|
|
27
|
+
const job = new CronJob(cronTime, callback);
|
|
28
|
+
this.schedulerRegistry.addCronJob(name, job);
|
|
29
|
+
job.start();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
deleteCronJob(name: string) {
|
|
33
|
+
this.schedulerRegistry.deleteCronJob(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Intervals and Timeouts
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Interval, Timeout } from '@nestjs/schedule';
|
|
42
|
+
|
|
43
|
+
@Injectable()
|
|
44
|
+
export class PollingService {
|
|
45
|
+
@Interval(10000) // Every 10 seconds
|
|
46
|
+
checkHealth() {
|
|
47
|
+
console.log('Health check');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Timeout(5000) // Run once after 5 seconds
|
|
51
|
+
onApplicationReady() {
|
|
52
|
+
console.log('Application warm-up complete');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Bull Queues
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// queue.module.ts
|
|
61
|
+
import { BullModule } from '@nestjs/bull';
|
|
62
|
+
|
|
63
|
+
@Module({
|
|
64
|
+
imports: [
|
|
65
|
+
BullModule.forRoot({
|
|
66
|
+
redis: { host: 'localhost', port: 6379 },
|
|
67
|
+
}),
|
|
68
|
+
BullModule.registerQueue({ name: 'email' }),
|
|
69
|
+
],
|
|
70
|
+
})
|
|
71
|
+
export class QueueModule {}
|
|
72
|
+
|
|
73
|
+
// email.processor.ts
|
|
74
|
+
import { Processor, Process, OnQueueFailed } from '@nestjs/bull';
|
|
75
|
+
import { Job } from 'bull';
|
|
76
|
+
|
|
77
|
+
@Processor('email')
|
|
78
|
+
export class EmailProcessor {
|
|
79
|
+
@Process()
|
|
80
|
+
async sendEmail(job: Job<{ to: string; subject: string }>) {
|
|
81
|
+
const { to, subject } = job.data;
|
|
82
|
+
await this.mailer.send(to, subject);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Process('welcome')
|
|
86
|
+
async sendWelcome(job: Job) {
|
|
87
|
+
// Named job processor
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@OnQueueFailed()
|
|
91
|
+
onFailed(job: Job, error: Error) {
|
|
92
|
+
console.error(`Job ${job.id} failed:`, error.message);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Usage in service
|
|
97
|
+
@Injectable()
|
|
98
|
+
export class EmailService {
|
|
99
|
+
constructor(@InjectQueue('email') private emailQueue: Queue) {}
|
|
100
|
+
|
|
101
|
+
async queueEmail(to: string, subject: string) {
|
|
102
|
+
await this.emailQueue.add({ to, subject }, {
|
|
103
|
+
attempts: 3,
|
|
104
|
+
backoff: { type: 'exponential', delay: 1000 },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Distributed Locking
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import Redlock from 'redlock';
|
|
114
|
+
|
|
115
|
+
@Injectable()
|
|
116
|
+
export class DistributedTaskService {
|
|
117
|
+
private redlock: Redlock;
|
|
118
|
+
|
|
119
|
+
constructor(private redis: Redis) {
|
|
120
|
+
this.redlock = new Redlock([redis]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@Cron('0 * * * *')
|
|
124
|
+
async runExclusiveTask() {
|
|
125
|
+
try {
|
|
126
|
+
const lock = await this.redlock.acquire(['task:hourly'], 30000);
|
|
127
|
+
try {
|
|
128
|
+
await this.doWork();
|
|
129
|
+
} finally {
|
|
130
|
+
await lock.release();
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
// Another instance is running the task
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: NestJS Search & CQRS
|
|
3
|
+
description: Elasticsearch integration and Sync patterns.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [nestjs, search, elasticsearch, cqrs]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/*.service.ts', '**/search/**']
|
|
8
|
+
keywords: [Elasticsearch, CQRS, Synchronization]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Search Engine & Full-Text
|
|
12
|
+
|
|
13
|
+
## Strategy
|
|
14
|
+
|
|
15
|
+
- **Pattern**: **CQRS (Command Query Responsibility Segregation)**.
|
|
16
|
+
- **Write**: To Primary Database (Postgres/MySQL). Source of Truth.
|
|
17
|
+
- **Read (Complex)**: To Search Engine (Elasticsearch, OpenSearch, MeiliSearch). Optimized for filtering, fuzzy search, and aggregation.
|
|
18
|
+
|
|
19
|
+
## Synchronization (The Hard Part)
|
|
20
|
+
|
|
21
|
+
- **Dual Write (Anti-Pattern)**: `await db.save(); await es.index();`.
|
|
22
|
+
- _Why_: Partial failures leave data inconsistent. Slows down HTTP response.
|
|
23
|
+
- **Event-Driven (Recommended)**:
|
|
24
|
+
1. Service writes to DB.
|
|
25
|
+
2. Service emits `EntityUpdated`.
|
|
26
|
+
3. Event Handler (Async) pushes to Queue (BullMQ).
|
|
27
|
+
4. Worker indexes document to Search Engine with retries.
|
|
28
|
+
- **CDC (Golden Standard)**: Change Data Capture (Debezium). Connects directly to DB transaction log. No app conceptual overhead, but higher ops complexity.
|
|
29
|
+
|
|
30
|
+
## Organization
|
|
31
|
+
|
|
32
|
+
- **Module**: Encapsulate the client in a `SearchModule`.
|
|
33
|
+
- **Abstraction**: Create generic `SearchService<T>` helpers.
|
|
34
|
+
- `indexDocument(id, body)`
|
|
35
|
+
- `search(query, filters)`
|
|
36
|
+
- **Mapping**: Use `class-transformer` to map Entities to "Search Documents". Search docs should be flatter than Relational entities constraints.
|
|
37
|
+
|
|
38
|
+
## Testing
|
|
39
|
+
|
|
40
|
+
- **E2E**: Do not mock the search engine in critical E2E flows.
|
|
41
|
+
- **Docker**: Spin up `elasticsearch:8` container in the test harness to verify indexing works.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# NestJS Search References
|
|
2
|
+
|
|
3
|
+
## References
|
|
4
|
+
|
|
5
|
+
- [**Search Patterns**](search-patterns.md) - Elasticsearch, full-text search, indexing
|
|
6
|
+
|
|
7
|
+
## Quick Checks
|
|
8
|
+
|
|
9
|
+
- [ ] Use dedicated search service
|
|
10
|
+
- [ ] Index relevant fields
|
|
11
|
+
- [ ] Implement pagination for results
|
|
12
|
+
- [ ] Handle search query sanitization
|
|
13
|
+
- [ ] Consider fuzzy matching for UX
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# NestJS Search Patterns
|
|
2
|
+
|
|
3
|
+
## Elasticsearch Integration
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { ElasticsearchModule, ElasticsearchService } from '@nestjs/elasticsearch';
|
|
7
|
+
|
|
8
|
+
@Module({
|
|
9
|
+
imports: [
|
|
10
|
+
ElasticsearchModule.register({
|
|
11
|
+
node: 'http://localhost:9200',
|
|
12
|
+
}),
|
|
13
|
+
],
|
|
14
|
+
})
|
|
15
|
+
export class SearchModule {}
|
|
16
|
+
|
|
17
|
+
@Injectable()
|
|
18
|
+
export class ProductSearchService {
|
|
19
|
+
constructor(private readonly esService: ElasticsearchService) {}
|
|
20
|
+
|
|
21
|
+
async indexProduct(product: Product) {
|
|
22
|
+
await this.esService.index({
|
|
23
|
+
index: 'products',
|
|
24
|
+
id: product.id,
|
|
25
|
+
document: {
|
|
26
|
+
name: product.name,
|
|
27
|
+
description: product.description,
|
|
28
|
+
category: product.category,
|
|
29
|
+
price: product.price,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async search(query: string, options?: { page?: number; limit?: number }) {
|
|
35
|
+
const { page = 1, limit = 10 } = options || {};
|
|
36
|
+
|
|
37
|
+
const result = await this.esService.search({
|
|
38
|
+
index: 'products',
|
|
39
|
+
from: (page - 1) * limit,
|
|
40
|
+
size: limit,
|
|
41
|
+
query: {
|
|
42
|
+
multi_match: {
|
|
43
|
+
query,
|
|
44
|
+
fields: ['name^3', 'description', 'category'],
|
|
45
|
+
fuzziness: 'AUTO',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
hits: result.hits.hits.map(hit => hit._source),
|
|
52
|
+
total: result.hits.total,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async deleteIndex(id: string) {
|
|
57
|
+
await this.esService.delete({ index: 'products', id });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## PostgreSQL Full-Text Search
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { Repository } from 'typeorm';
|
|
66
|
+
|
|
67
|
+
@Injectable()
|
|
68
|
+
export class ArticleSearchService {
|
|
69
|
+
constructor(
|
|
70
|
+
@InjectRepository(Article)
|
|
71
|
+
private articleRepo: Repository<Article>,
|
|
72
|
+
) {}
|
|
73
|
+
|
|
74
|
+
async search(query: string) {
|
|
75
|
+
return this.articleRepo
|
|
76
|
+
.createQueryBuilder('article')
|
|
77
|
+
.where(
|
|
78
|
+
`to_tsvector('english', article.title || ' ' || article.content) @@ plainto_tsquery('english', :query)`,
|
|
79
|
+
{ query },
|
|
80
|
+
)
|
|
81
|
+
.orderBy(
|
|
82
|
+
`ts_rank(to_tsvector('english', article.title || ' ' || article.content), plainto_tsquery('english', :query))`,
|
|
83
|
+
'DESC',
|
|
84
|
+
)
|
|
85
|
+
.getMany();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Migration for GIN index
|
|
90
|
+
export class AddSearchIndex implements MigrationInterface {
|
|
91
|
+
async up(queryRunner: QueryRunner) {
|
|
92
|
+
await queryRunner.query(`
|
|
93
|
+
CREATE INDEX idx_article_search
|
|
94
|
+
ON article
|
|
95
|
+
USING GIN (to_tsvector('english', title || ' ' || content))
|
|
96
|
+
`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Search with Filters
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
async advancedSearch(params: SearchParams) {
|
|
105
|
+
const { query, category, minPrice, maxPrice, sort } = params;
|
|
106
|
+
|
|
107
|
+
const must = [];
|
|
108
|
+
const filter = [];
|
|
109
|
+
|
|
110
|
+
if (query) {
|
|
111
|
+
must.push({
|
|
112
|
+
multi_match: { query, fields: ['name', 'description'] },
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (category) {
|
|
117
|
+
filter.push({ term: { category } });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (minPrice || maxPrice) {
|
|
121
|
+
filter.push({
|
|
122
|
+
range: {
|
|
123
|
+
price: {
|
|
124
|
+
...(minPrice && { gte: minPrice }),
|
|
125
|
+
...(maxPrice && { lte: maxPrice }),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return this.esService.search({
|
|
132
|
+
index: 'products',
|
|
133
|
+
query: { bool: { must, filter } },
|
|
134
|
+
sort: sort ? [{ [sort.field]: sort.order }] : undefined,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: NestJS Security
|
|
3
|
+
description: Authentication, RBAC, and Hardening standards.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [nestjs, security, auth, jwt]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/*.guard.ts', '**/*.strategy.ts', '**/auth/**']
|
|
8
|
+
keywords: [Passport, JWT, AuthGuard, CSRF, Helmet]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# NestJS Security Standards
|
|
12
|
+
|
|
13
|
+
## Authentication
|
|
14
|
+
|
|
15
|
+
- **Strategies**: Use `@nestjs/passport` with `passport-jwt`.
|
|
16
|
+
- **JWT Hardening**:
|
|
17
|
+
- **Algorithm**: Enforce `algorithms: ['RS256']` (preferred) or `['HS256']`. **Reject `none`**.
|
|
18
|
+
- **Claims**: Validate `iss` (Issuer) and `aud` (Audience).
|
|
19
|
+
- **Secrets**: High entropy (> 256-bit) for HS256.
|
|
20
|
+
- **State**: Stateless. Short access tokens (15m), Long httponly refresh tokens (7d).
|
|
21
|
+
- **MFA**: Require 2FA for sensitive access (admin panels).
|
|
22
|
+
|
|
23
|
+
## Authorization (RBAC)
|
|
24
|
+
|
|
25
|
+
- **Strategy**: **Deny by default**.
|
|
26
|
+
- **Implementation**: Bind `AuthGuard` globally (APP_GUARD).
|
|
27
|
+
- **Bypass**: Create a `@Public()` decorator and allow access if present in the Guard.
|
|
28
|
+
- **Metadata**: Use `Reflector.createDecorator<string[]>()`.
|
|
29
|
+
- **Guards**: Use `Reflector` to merge Method/Class roles (`getAllAndOverride`).
|
|
30
|
+
|
|
31
|
+
## Cryptography & Hashing
|
|
32
|
+
|
|
33
|
+
- **Hashing**:
|
|
34
|
+
- **Algorithm**: Use **Argon2id** (`argon2`) instead of Bcrypt (vulnerable to GPU/FPGA cracking).
|
|
35
|
+
- **Implementation**: `await argon2.hash(password)`.
|
|
36
|
+
- **Encryption** (At Rest):
|
|
37
|
+
- **Algorithm**: Use **AES-256-GCM** (Authenticated Encryption).
|
|
38
|
+
- **Keys**: Never hardcode. Rotate keys using a KMS (Key Management Service).
|
|
39
|
+
- **Native**: Use Node.js `crypto.createCipheriv`.
|
|
40
|
+
|
|
41
|
+
## CSRF (Cross-Site Request Forgery)
|
|
42
|
+
|
|
43
|
+
- **Context**: Mandatory if using **Cookie-based sessions** or **Cookie-based JWTs**.
|
|
44
|
+
- **Mitigation**:
|
|
45
|
+
- **Synchronizer Token**: Use `csurf` via `@nest-middlewares/csurf` or similar wrapper.
|
|
46
|
+
- **State**: Token must be cryptographically strong and verified on every state-changing request (POST/PUT/DELETE).
|
|
47
|
+
- **Note**: If using strictly `Authorization: Bearer` headers (localStorage), CSRF is less critical but `SameSite: Strict` cookies are still recommended for defense-in-depth.
|
|
48
|
+
|
|
49
|
+
## Hardening
|
|
50
|
+
|
|
51
|
+
1. **Helmet**: Mandatory. `app.use(helmet())`.
|
|
52
|
+
- **HSTS**: Enable `Strict-Transport-Security` with `preload`.
|
|
53
|
+
- **CSP**: Configure Content Security Policy specifically if serving any UI.
|
|
54
|
+
2. **Permissions-Policy**: Restrict browser permissions via `helmet.permissionsPolicy()`.
|
|
55
|
+
3. **CORS**: Explicit origins only. No `*`.
|
|
56
|
+
4. **Throttling**:
|
|
57
|
+
- **Distributed**: Do not use in-memory rate limiting in production. Use `@nestjs/throttler` with **Redis storage** (`throttler-storage-redis`) to sync limits across instances.
|
|
58
|
+
|
|
59
|
+
## Audit Logging
|
|
60
|
+
|
|
61
|
+
- **Requirement**: Track critical mutations (Who, What, When).
|
|
62
|
+
- **Pattern**: Implement an `AuditInterceptor` that logs `POST/PUT/DELETE` actions to a secure, immutable log store (separate from app logs).
|
|
63
|
+
|
|
64
|
+
## Secrets & Config
|
|
65
|
+
|
|
66
|
+
- **CI/CD**: Run `npm audit` or `pnpm audit --prod` in pipelines.
|
|
67
|
+
- **Runtime**: Avoid `.env` files in production runtime. Inject secrets via environment variables from a vault (AWS Secret Manager / HashiCorp Vault) into the container environment.
|
|
68
|
+
|
|
69
|
+
## Data Sanitization
|
|
70
|
+
|
|
71
|
+
- **Transform**: Use `ClassSerializerInterceptor` + `@Exclude()` globally or per-controller to strip sensitive fields (passwords) from responses.
|
|
72
|
+
- **Validation**: `ValidationPipe({ whitelist: true })` prevents mass assignment attacks by stripping unknown properties from payloads.
|
|
73
|
+
|
|
74
|
+
## Improper Assets Management
|
|
75
|
+
|
|
76
|
+
- **Shadow APIs**: Audit routes regularly.
|
|
77
|
+
- **Deprecation**: Disable Swagger/OpenAPI endpoints (`/api`, `/docs`) in **production**.
|
|
78
|
+
|
|
79
|
+
## Server-Side Request Forgery (SSRF)
|
|
80
|
+
|
|
81
|
+
- **Validation**: Validate/Allowlist domains for **all** outgoing HTTP requests (`HttpService`).
|
|
82
|
+
- **Network**: Restrict egress traffic (e.g., block AWS metadata `169.254.169.254`) via infrastructure/firewall.
|
|
83
|
+
|
|
84
|
+
## Injection Prevention
|
|
85
|
+
|
|
86
|
+
- **SQLi**: Prefer ORM/ODM methods. **Avoid raw queries** (`query()`) with string concatenation. Use parameterized queries if raw SQL is strictly necessary.
|
|
87
|
+
- **XSS**: Input validation is not enough. Sanitize HTML input (e.g., `dompurify`) before storage or output.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# NestJS Security References
|
|
2
|
+
|
|
3
|
+
## References
|
|
4
|
+
|
|
5
|
+
- [**Authentication**](authentication.md) - JWT, Passport, sessions
|
|
6
|
+
- [**Authorization**](authorization.md) - Guards, RBAC, policies
|
|
7
|
+
|
|
8
|
+
## Quick Checks
|
|
9
|
+
|
|
10
|
+
- [ ] Use Passport strategies for auth
|
|
11
|
+
- [ ] JWT with proper expiration
|
|
12
|
+
- [ ] Guards for route protection
|
|
13
|
+
- [ ] RBAC or ABAC for permissions
|
|
14
|
+
- [ ] Rate limiting for API protection
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# NestJS Authentication
|
|
2
|
+
|
|
3
|
+
## JWT Strategy
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Injectable } from '@nestjs/common';
|
|
7
|
+
import { PassportStrategy } from '@nestjs/passport';
|
|
8
|
+
import { ExtractJwt, Strategy } from 'passport-jwt';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
12
|
+
constructor(private configService: ConfigService) {
|
|
13
|
+
super({
|
|
14
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
15
|
+
ignoreExpiration: false,
|
|
16
|
+
secretOrKey: configService.get('JWT_SECRET'),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async validate(payload: JwtPayload) {
|
|
21
|
+
return { userId: payload.sub, email: payload.email, roles: payload.roles };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Auth Service
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { JwtService } from '@nestjs/jwt';
|
|
30
|
+
import * as bcrypt from 'bcrypt';
|
|
31
|
+
|
|
32
|
+
@Injectable()
|
|
33
|
+
export class AuthService {
|
|
34
|
+
constructor(
|
|
35
|
+
private usersService: UsersService,
|
|
36
|
+
private jwtService: JwtService,
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
async validateUser(email: string, password: string): Promise<User | null> {
|
|
40
|
+
const user = await this.usersService.findByEmail(email);
|
|
41
|
+
if (user && await bcrypt.compare(password, user.password)) {
|
|
42
|
+
return user;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async login(user: User) {
|
|
48
|
+
const payload = { email: user.email, sub: user.id, roles: user.roles };
|
|
49
|
+
return {
|
|
50
|
+
access_token: this.jwtService.sign(payload),
|
|
51
|
+
refresh_token: this.jwtService.sign(payload, { expiresIn: '7d' }),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async refreshToken(token: string) {
|
|
56
|
+
const payload = this.jwtService.verify(token);
|
|
57
|
+
const user = await this.usersService.findById(payload.sub);
|
|
58
|
+
return this.login(user);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Auth Guard
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { Injectable, ExecutionContext } from '@nestjs/common';
|
|
67
|
+
import { AuthGuard } from '@nestjs/passport';
|
|
68
|
+
import { Reflector } from '@nestjs/core';
|
|
69
|
+
|
|
70
|
+
@Injectable()
|
|
71
|
+
export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
72
|
+
constructor(private reflector: Reflector) {
|
|
73
|
+
super();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
canActivate(context: ExecutionContext) {
|
|
77
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
|
|
78
|
+
context.getHandler(),
|
|
79
|
+
context.getClass(),
|
|
80
|
+
]);
|
|
81
|
+
if (isPublic) return true;
|
|
82
|
+
return super.canActivate(context);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Public decorator
|
|
87
|
+
export const Public = () => SetMetadata('isPublic', true);
|
|
88
|
+
|
|
89
|
+
// Usage
|
|
90
|
+
@Controller('users')
|
|
91
|
+
@UseGuards(JwtAuthGuard)
|
|
92
|
+
export class UsersController {
|
|
93
|
+
@Public()
|
|
94
|
+
@Get('public')
|
|
95
|
+
publicRoute() {}
|
|
96
|
+
|
|
97
|
+
@Get('protected')
|
|
98
|
+
protectedRoute(@CurrentUser() user: User) {}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Roles Guard
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { SetMetadata } from '@nestjs/common';
|
|
106
|
+
|
|
107
|
+
export const ROLES_KEY = 'roles';
|
|
108
|
+
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
|
|
109
|
+
|
|
110
|
+
@Injectable()
|
|
111
|
+
export class RolesGuard implements CanActivate {
|
|
112
|
+
constructor(private reflector: Reflector) {}
|
|
113
|
+
|
|
114
|
+
canActivate(context: ExecutionContext): boolean {
|
|
115
|
+
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
|
|
116
|
+
context.getHandler(),
|
|
117
|
+
context.getClass(),
|
|
118
|
+
]);
|
|
119
|
+
if (!requiredRoles) return true;
|
|
120
|
+
|
|
121
|
+
const { user } = context.switchToHttp().getRequest();
|
|
122
|
+
return requiredRoles.some(role => user.roles?.includes(role));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Usage
|
|
127
|
+
@Post()
|
|
128
|
+
@Roles(Role.Admin)
|
|
129
|
+
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
130
|
+
create() {}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Module Setup
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
@Module({
|
|
137
|
+
imports: [
|
|
138
|
+
PassportModule,
|
|
139
|
+
JwtModule.registerAsync({
|
|
140
|
+
inject: [ConfigService],
|
|
141
|
+
useFactory: (config: ConfigService) => ({
|
|
142
|
+
secret: config.get('JWT_SECRET'),
|
|
143
|
+
signOptions: { expiresIn: '1h' },
|
|
144
|
+
}),
|
|
145
|
+
}),
|
|
146
|
+
],
|
|
147
|
+
providers: [AuthService, JwtStrategy, JwtAuthGuard, RolesGuard],
|
|
148
|
+
exports: [AuthService],
|
|
149
|
+
})
|
|
150
|
+
export class AuthModule {}
|
|
151
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: NestJS Testing
|
|
3
|
+
description: Unit and E2E testing strategies with Docker.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [nestjs, testing, jest, e2e]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/*.spec.ts', 'test/**']
|
|
8
|
+
keywords: [Test.createTestingModule, supertest, jest]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# NestJS Testing Standards
|
|
12
|
+
|
|
13
|
+
## Strategy
|
|
14
|
+
|
|
15
|
+
1. **Unit Tests**: Isolated logic (Services). Mock **all** dependencies (`jest.fn()`).
|
|
16
|
+
2. **E2E Tests**: Full lifecycle (`test/app.e2e-spec.ts`).
|
|
17
|
+
- **Rule**: Use a **real** Test Database (Docker). Never mock the database in E2E.
|
|
18
|
+
- **Idempotency**: **Mandatory** cleanup. Use a transaction rollback strategy or explicit `TRUNCATE` in `afterEach` to ensure tests don't leak state.
|
|
19
|
+
|
|
20
|
+
## E2E Best Practices (Pro)
|
|
21
|
+
|
|
22
|
+
- **Overriding**: Use `.overrideProvider(AuthGuard).useValue({ canActivate: () => true })` to bypass security in functional flow tests.
|
|
23
|
+
- **Factories**: Use Factory patterns (e.g. `mockUserFactory()`) to generate DTOs/Entities. Avoid hardcoded JSON literals.
|
|
24
|
+
|
|
25
|
+
## Tools
|
|
26
|
+
|
|
27
|
+
- **Runner**: Jest (swc/ts-jest).
|
|
28
|
+
- **Mocks**: `jest.fn()`, `jest.spyOn()`.
|
|
29
|
+
|
|
30
|
+
## Setup Pattern
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// Standard Setup
|
|
34
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
35
|
+
providers: [
|
|
36
|
+
UsersService,
|
|
37
|
+
{ provide: getRepositoryToken(User), useValue: mockRepo },
|
|
38
|
+
],
|
|
39
|
+
}).compile();
|
|
40
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# NestJS Testing References
|
|
2
|
+
|
|
3
|
+
## References
|
|
4
|
+
|
|
5
|
+
- [**Unit Testing**](unit-testing.md) - Services, mocking, Jest patterns
|
|
6
|
+
- [**E2E Testing**](e2e-testing.md) - Supertest, test modules
|
|
7
|
+
|
|
8
|
+
## Quick Checks
|
|
9
|
+
|
|
10
|
+
- [ ] Use Test.createTestingModule for unit tests
|
|
11
|
+
- [ ] Mock dependencies with jest.mock or providers
|
|
12
|
+
- [ ] E2E tests with supertest
|
|
13
|
+
- [ ] Test database with in-memory or test containers
|
|
14
|
+
- [ ] Coverage thresholds in jest config
|