@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,179 @@
|
|
|
1
|
+
# NestJS Unit Testing
|
|
2
|
+
|
|
3
|
+
## Service Testing
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
7
|
+
|
|
8
|
+
describe('UsersService', () => {
|
|
9
|
+
let service: UsersService;
|
|
10
|
+
let repository: MockType<Repository<User>>;
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
14
|
+
providers: [
|
|
15
|
+
UsersService,
|
|
16
|
+
{
|
|
17
|
+
provide: getRepositoryToken(User),
|
|
18
|
+
useFactory: repositoryMockFactory,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
}).compile();
|
|
22
|
+
|
|
23
|
+
service = module.get<UsersService>(UsersService);
|
|
24
|
+
repository = module.get(getRepositoryToken(User));
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('findOne', () => {
|
|
28
|
+
it('should return a user', async () => {
|
|
29
|
+
const user = { id: 1, name: 'John', email: 'john@test.com' };
|
|
30
|
+
repository.findOne.mockResolvedValue(user);
|
|
31
|
+
|
|
32
|
+
const result = await service.findOne(1);
|
|
33
|
+
|
|
34
|
+
expect(result).toEqual(user);
|
|
35
|
+
expect(repository.findOne).toHaveBeenCalledWith({ where: { id: 1 } });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should throw NotFoundException', async () => {
|
|
39
|
+
repository.findOne.mockResolvedValue(null);
|
|
40
|
+
|
|
41
|
+
await expect(service.findOne(999)).rejects.toThrow(NotFoundException);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Mock factory
|
|
47
|
+
const repositoryMockFactory = () => ({
|
|
48
|
+
find: jest.fn(),
|
|
49
|
+
findOne: jest.fn(),
|
|
50
|
+
save: jest.fn(),
|
|
51
|
+
create: jest.fn(),
|
|
52
|
+
update: jest.fn(),
|
|
53
|
+
delete: jest.fn(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
type MockType<T> = {
|
|
57
|
+
[P in keyof T]?: jest.Mock;
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Controller Testing
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
describe('UsersController', () => {
|
|
65
|
+
let controller: UsersController;
|
|
66
|
+
let service: UsersService;
|
|
67
|
+
|
|
68
|
+
beforeEach(async () => {
|
|
69
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
70
|
+
controllers: [UsersController],
|
|
71
|
+
providers: [
|
|
72
|
+
{
|
|
73
|
+
provide: UsersService,
|
|
74
|
+
useValue: {
|
|
75
|
+
findAll: jest.fn(),
|
|
76
|
+
findOne: jest.fn(),
|
|
77
|
+
create: jest.fn(),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
}).compile();
|
|
82
|
+
|
|
83
|
+
controller = module.get<UsersController>(UsersController);
|
|
84
|
+
service = module.get<UsersService>(UsersService);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('findAll', () => {
|
|
88
|
+
it('should return an array of users', async () => {
|
|
89
|
+
const users = [{ id: 1, name: 'John' }];
|
|
90
|
+
jest.spyOn(service, 'findAll').mockResolvedValue(users);
|
|
91
|
+
|
|
92
|
+
expect(await controller.findAll()).toBe(users);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('create', () => {
|
|
97
|
+
it('should create a user', async () => {
|
|
98
|
+
const dto = { name: 'John', email: 'john@test.com' };
|
|
99
|
+
const user = { id: 1, ...dto };
|
|
100
|
+
jest.spyOn(service, 'create').mockResolvedValue(user);
|
|
101
|
+
|
|
102
|
+
expect(await controller.create(dto)).toBe(user);
|
|
103
|
+
expect(service.create).toHaveBeenCalledWith(dto);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Mocking Patterns
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Mock entire module
|
|
113
|
+
jest.mock('../services/email.service');
|
|
114
|
+
|
|
115
|
+
// Partial mock
|
|
116
|
+
jest.mock('../services/email.service', () => ({
|
|
117
|
+
...jest.requireActual('../services/email.service'),
|
|
118
|
+
sendEmail: jest.fn(),
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
// Spy on method
|
|
122
|
+
const spy = jest.spyOn(service, 'method');
|
|
123
|
+
spy.mockResolvedValue(result);
|
|
124
|
+
|
|
125
|
+
// Mock implementation
|
|
126
|
+
mockService.method.mockImplementation((arg) => {
|
|
127
|
+
return arg === 'special' ? specialResult : defaultResult;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Reset mocks
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
jest.clearAllMocks();
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## E2E Testing
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
140
|
+
import { INestApplication } from '@nestjs/common';
|
|
141
|
+
import * as request from 'supertest';
|
|
142
|
+
|
|
143
|
+
describe('UsersController (e2e)', () => {
|
|
144
|
+
let app: INestApplication;
|
|
145
|
+
|
|
146
|
+
beforeAll(async () => {
|
|
147
|
+
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
148
|
+
imports: [AppModule],
|
|
149
|
+
}).compile();
|
|
150
|
+
|
|
151
|
+
app = moduleFixture.createNestApplication();
|
|
152
|
+
await app.init();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
afterAll(async () => {
|
|
156
|
+
await app.close();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('/users (GET)', () => {
|
|
160
|
+
return request(app.getHttpServer())
|
|
161
|
+
.get('/users')
|
|
162
|
+
.expect(200)
|
|
163
|
+
.expect((res) => {
|
|
164
|
+
expect(Array.isArray(res.body)).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('/users (POST)', () => {
|
|
169
|
+
return request(app.getHttpServer())
|
|
170
|
+
.post('/users')
|
|
171
|
+
.send({ name: 'John', email: 'john@test.com' })
|
|
172
|
+
.expect(201)
|
|
173
|
+
.expect((res) => {
|
|
174
|
+
expect(res.body).toHaveProperty('id');
|
|
175
|
+
expect(res.body.name).toBe('John');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: NestJS Microservices
|
|
3
|
+
description: gRPC, RabbitMQ standards and Monorepo contracts.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [nestjs, microservices, grpc, rabbitmq]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['main.ts', '**/*.controller.ts']
|
|
8
|
+
keywords: [Transport.GRPC, Transport.RMQ, MicroserviceOptions]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Microservices & Transport Standards
|
|
12
|
+
|
|
13
|
+
## Transport Strategies
|
|
14
|
+
|
|
15
|
+
- **Synchronous (RPC)**: Use **gRPC** for low-latency, internal service-to-service calls.
|
|
16
|
+
- **Why**: 10x faster than REST/JSON, centralized `.proto` contracts.
|
|
17
|
+
- **Asynchronous (Events)**: Use **RabbitMQ** or **Kafka** for decoupling domains.
|
|
18
|
+
- **Pattern**: Fire-and-forget (`emit()`) for side effects (e.g., "UserCreated" -> "SendEmail").
|
|
19
|
+
|
|
20
|
+
## Monorepo Architecture
|
|
21
|
+
|
|
22
|
+
- **Contracts**:
|
|
23
|
+
- **Pattern**: Store all DTOs, `.proto` files, and Interfaces in a **Shared Library** (`libs/contracts`).
|
|
24
|
+
- **Rule**: Services never import code from other services. They only import from `contracts`.
|
|
25
|
+
- **Versioning**: Semantic versioning of messages is mandatory. Never change a field type; add a new field.
|
|
26
|
+
|
|
27
|
+
## Exception Handling
|
|
28
|
+
|
|
29
|
+
- **Propagation**: Standard `HttpException` is lost over Rpc/Tcp.
|
|
30
|
+
- **Standard**: Use `RpcException` and generic Filters.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// Global RPC Filter
|
|
34
|
+
@Catch()
|
|
35
|
+
export class RpcExceptionFilter implements RpcExceptionFilter<RpcException> {
|
|
36
|
+
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
|
|
37
|
+
return throwError(() => exception.getError());
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Serialization
|
|
43
|
+
|
|
44
|
+
- **Message DTOs**: Use `class-validator` just like HTTP.
|
|
45
|
+
- **Config**: Apply `useGlobalPipes(new ValidationPipe({ transform: true }))` in the `MicroserviceOptions` setup, not just HTTP app setup.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# NestJS Transport References
|
|
2
|
+
|
|
3
|
+
## References
|
|
4
|
+
|
|
5
|
+
- [**Microservices Patterns**](microservices-patterns.md) - TCP, Redis, RabbitMQ, Kafka, gRPC
|
|
6
|
+
|
|
7
|
+
## Quick Checks
|
|
8
|
+
|
|
9
|
+
- [ ] Choose appropriate transport for use case
|
|
10
|
+
- [ ] Implement proper error handling
|
|
11
|
+
- [ ] Use message patterns consistently
|
|
12
|
+
- [ ] Configure retries and timeouts
|
|
13
|
+
- [ ] Monitor message processing
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# NestJS Microservices Transport Patterns
|
|
2
|
+
|
|
3
|
+
## TCP Transport
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Microservice
|
|
7
|
+
import { NestFactory } from '@nestjs/core';
|
|
8
|
+
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
|
|
9
|
+
|
|
10
|
+
async function bootstrap() {
|
|
11
|
+
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
|
|
12
|
+
AppModule,
|
|
13
|
+
{
|
|
14
|
+
transport: Transport.TCP,
|
|
15
|
+
options: { host: '0.0.0.0', port: 3001 },
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
await app.listen();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Handler
|
|
22
|
+
@Controller()
|
|
23
|
+
export class AppController {
|
|
24
|
+
@MessagePattern({ cmd: 'get_user' })
|
|
25
|
+
getUser(data: { id: string }) {
|
|
26
|
+
return this.userService.findOne(data.id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@EventPattern('user_created')
|
|
30
|
+
handleUserCreated(data: User) {
|
|
31
|
+
console.log('User created:', data);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Client
|
|
36
|
+
@Injectable()
|
|
37
|
+
export class ClientService {
|
|
38
|
+
constructor(
|
|
39
|
+
@Inject('USER_SERVICE') private client: ClientProxy,
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
getUser(id: string) {
|
|
43
|
+
return this.client.send({ cmd: 'get_user' }, { id });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
emitUserCreated(user: User) {
|
|
47
|
+
this.client.emit('user_created', user);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Redis Transport
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// Configuration
|
|
56
|
+
{
|
|
57
|
+
transport: Transport.REDIS,
|
|
58
|
+
options: {
|
|
59
|
+
host: 'localhost',
|
|
60
|
+
port: 6379,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Module registration
|
|
65
|
+
@Module({
|
|
66
|
+
imports: [
|
|
67
|
+
ClientsModule.register([{
|
|
68
|
+
name: 'REDIS_SERVICE',
|
|
69
|
+
transport: Transport.REDIS,
|
|
70
|
+
options: { host: 'localhost', port: 6379 },
|
|
71
|
+
}]),
|
|
72
|
+
],
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## RabbitMQ Transport
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Configuration
|
|
80
|
+
{
|
|
81
|
+
transport: Transport.RMQ,
|
|
82
|
+
options: {
|
|
83
|
+
urls: ['amqp://localhost:5672'],
|
|
84
|
+
queue: 'orders_queue',
|
|
85
|
+
queueOptions: { durable: true },
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handler with acknowledgment
|
|
90
|
+
@MessagePattern('process_order')
|
|
91
|
+
async processOrder(@Payload() data: Order, @Ctx() context: RmqContext) {
|
|
92
|
+
const channel = context.getChannelRef();
|
|
93
|
+
const originalMsg = context.getMessage();
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
await this.orderService.process(data);
|
|
97
|
+
channel.ack(originalMsg);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
channel.nack(originalMsg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Kafka Transport
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Configuration
|
|
108
|
+
{
|
|
109
|
+
transport: Transport.KAFKA,
|
|
110
|
+
options: {
|
|
111
|
+
client: {
|
|
112
|
+
brokers: ['localhost:9092'],
|
|
113
|
+
},
|
|
114
|
+
consumer: {
|
|
115
|
+
groupId: 'orders-consumer',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handler
|
|
121
|
+
@MessagePattern('orders.created')
|
|
122
|
+
handleOrderCreated(@Payload() message: Order, @Ctx() context: KafkaContext) {
|
|
123
|
+
const { offset, partition } = context.getMessage();
|
|
124
|
+
console.log(`Processing offset ${offset} from partition ${partition}`);
|
|
125
|
+
return this.orderService.process(message);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## gRPC Transport
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Configuration
|
|
133
|
+
{
|
|
134
|
+
transport: Transport.GRPC,
|
|
135
|
+
options: {
|
|
136
|
+
package: 'hero',
|
|
137
|
+
protoPath: join(__dirname, 'hero.proto'),
|
|
138
|
+
url: 'localhost:5000',
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Service implementation
|
|
143
|
+
@GrpcMethod('HeroService', 'FindOne')
|
|
144
|
+
findOne(data: { id: number }): Hero {
|
|
145
|
+
return this.heroService.findOne(data.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Streaming
|
|
149
|
+
@GrpcStreamMethod('HeroService', 'FindMany')
|
|
150
|
+
findMany(data$: Observable<HeroById>): Observable<Hero> {
|
|
151
|
+
return data$.pipe(
|
|
152
|
+
mergeMap(({ id }) => this.heroService.findOne(id)),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Hybrid Application
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
async function bootstrap() {
|
|
161
|
+
const app = await NestFactory.create(AppModule);
|
|
162
|
+
|
|
163
|
+
// HTTP
|
|
164
|
+
app.connectMicroservice({ transport: Transport.TCP, options: { port: 3001 } });
|
|
165
|
+
app.connectMicroservice({ transport: Transport.REDIS, options: { host: 'localhost' } });
|
|
166
|
+
|
|
167
|
+
await app.startAllMicroservices();
|
|
168
|
+
await app.listen(3000);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Next.js App Router
|
|
3
|
+
description: File-system routing, Layouts, and Route Groups.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [nextjs, routing, app-router]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['app/**/page.tsx', 'app/**/layout.tsx', 'app/**/loading.tsx']
|
|
8
|
+
keywords: [App Router, Layout, Route Group, parallel routes]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Next.js App Router
|
|
12
|
+
|
|
13
|
+
## **Priority: P0 (CRITICAL)**
|
|
14
|
+
|
|
15
|
+
Use the App Router (`app/` directory) for all new projects.
|
|
16
|
+
|
|
17
|
+
## File Conventions
|
|
18
|
+
|
|
19
|
+
- **page.tsx**: The UI for a route.
|
|
20
|
+
- **layout.tsx**: Shared UI wrapping children. Persists across navigation.
|
|
21
|
+
- **template.tsx**: Similar to layout, but remounts on navigation.
|
|
22
|
+
- **loading.tsx**: Suspense boundary for loading states.
|
|
23
|
+
- **error.tsx**: Error boundary (Must be Client Component).
|
|
24
|
+
- **not-found.tsx**: Custom 404 UI.
|
|
25
|
+
- **route.ts**: Server-side API endpoint.
|
|
26
|
+
|
|
27
|
+
## Structure Patterns
|
|
28
|
+
|
|
29
|
+
- **Route Groups**: Use parenthesis `(auth)` to organize routes without affecting URL path.
|
|
30
|
+
- `app/(auth)/login/page.tsx` -> `/login`
|
|
31
|
+
- **Private Folders**: Use underscore `_lib` to exclude folders from routing.
|
|
32
|
+
- **Dynamic Routes**: Use brackets `[slug]`.
|
|
33
|
+
- `app/blog/[slug]/page.tsx` -> `/blog/abc`
|
|
34
|
+
- **Catch-all**: `[...slug]` matches subsequent segments.
|
|
35
|
+
|
|
36
|
+
## Navigation
|
|
37
|
+
|
|
38
|
+
- **Link Component**: Use `<Link href="/dashboard">` for prefetching.
|
|
39
|
+
- **Programmatic**: Use `useRouter` hook (Client Components only).
|
|
40
|
+
- `router.push('/dashboard')`
|
|
41
|
+
- **Redirect**: Use `redirect('/login')` in Server Components.
|
|
42
|
+
|
|
43
|
+
## Best Practices
|
|
44
|
+
|
|
45
|
+
- **Colocation**: Keep component files, styles, and tests inside the route folder.
|
|
46
|
+
- **Layouts**: Use Root Layout (`app/layout.tsx`) for `<html>` and `<body>` tags.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Next.js App Router References
|
|
2
|
+
|
|
3
|
+
## References
|
|
4
|
+
|
|
5
|
+
- [**Routing Patterns**](routing-patterns.md) - Routes, layouts, loading, error
|
|
6
|
+
- [**Navigation**](navigation.md) - Link, useRouter, redirects
|
|
7
|
+
|
|
8
|
+
## Quick Checks
|
|
9
|
+
|
|
10
|
+
- [ ] Use folder-based routing
|
|
11
|
+
- [ ] Layouts for shared UI
|
|
12
|
+
- [ ] loading.tsx for suspense
|
|
13
|
+
- [ ] error.tsx for error boundaries
|
|
14
|
+
- [ ] Route groups for organization
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Next.js App Router Patterns
|
|
2
|
+
|
|
3
|
+
## File Conventions
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
app/
|
|
7
|
+
├── layout.tsx # Root layout (required)
|
|
8
|
+
├── page.tsx # Home page (/)
|
|
9
|
+
├── loading.tsx # Loading UI
|
|
10
|
+
├── error.tsx # Error boundary
|
|
11
|
+
├── not-found.tsx # 404 page
|
|
12
|
+
├── users/
|
|
13
|
+
│ ├── page.tsx # /users
|
|
14
|
+
│ ├── [id]/
|
|
15
|
+
│ │ └── page.tsx # /users/:id
|
|
16
|
+
│ └── [...slug]/
|
|
17
|
+
│ └── page.tsx # /users/* (catch-all)
|
|
18
|
+
├── (marketing)/ # Route group (no URL impact)
|
|
19
|
+
│ ├── about/
|
|
20
|
+
│ └── contact/
|
|
21
|
+
└── @modal/ # Parallel route
|
|
22
|
+
└── login/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Layouts
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// app/layout.tsx - Root layout
|
|
29
|
+
export default function RootLayout({
|
|
30
|
+
children,
|
|
31
|
+
}: {
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
}) {
|
|
34
|
+
return (
|
|
35
|
+
<html lang="en">
|
|
36
|
+
<body>
|
|
37
|
+
<Header />
|
|
38
|
+
<main>{children}</main>
|
|
39
|
+
<Footer />
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// app/dashboard/layout.tsx - Nested layout
|
|
46
|
+
export default function DashboardLayout({
|
|
47
|
+
children,
|
|
48
|
+
}: {
|
|
49
|
+
children: React.ReactNode;
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<div className="dashboard">
|
|
53
|
+
<Sidebar />
|
|
54
|
+
<div className="content">{children}</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Dynamic Routes
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// app/users/[id]/page.tsx
|
|
64
|
+
export default async function UserPage({
|
|
65
|
+
params,
|
|
66
|
+
}: {
|
|
67
|
+
params: Promise<{ id: string }>;
|
|
68
|
+
}) {
|
|
69
|
+
const { id } = await params;
|
|
70
|
+
const user = await getUser(id);
|
|
71
|
+
return <UserProfile user={user} />;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Generate static params
|
|
75
|
+
export async function generateStaticParams() {
|
|
76
|
+
const users = await getUsers();
|
|
77
|
+
return users.map((user) => ({ id: user.id }));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Catch-all routes
|
|
81
|
+
// app/docs/[...slug]/page.tsx
|
|
82
|
+
export default async function DocsPage({
|
|
83
|
+
params,
|
|
84
|
+
}: {
|
|
85
|
+
params: Promise<{ slug: string[] }>;
|
|
86
|
+
}) {
|
|
87
|
+
const { slug } = await params;
|
|
88
|
+
// slug = ['getting-started', 'installation']
|
|
89
|
+
// for /docs/getting-started/installation
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Loading & Error States
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// app/users/loading.tsx
|
|
97
|
+
export default function Loading() {
|
|
98
|
+
return <UsersSkeleton />;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// app/users/error.tsx
|
|
102
|
+
'use client';
|
|
103
|
+
|
|
104
|
+
export default function Error({
|
|
105
|
+
error,
|
|
106
|
+
reset,
|
|
107
|
+
}: {
|
|
108
|
+
error: Error;
|
|
109
|
+
reset: () => void;
|
|
110
|
+
}) {
|
|
111
|
+
return (
|
|
112
|
+
<div>
|
|
113
|
+
<h2>Something went wrong!</h2>
|
|
114
|
+
<button onClick={() => reset()}>Try again</button>
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// app/users/not-found.tsx
|
|
120
|
+
export default function NotFound() {
|
|
121
|
+
return <div>User not found</div>;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Route Groups
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
app/
|
|
129
|
+
├── (auth)/
|
|
130
|
+
│ ├── layout.tsx # Auth layout (no header)
|
|
131
|
+
│ ├── login/
|
|
132
|
+
│ └── register/
|
|
133
|
+
├── (main)/
|
|
134
|
+
│ ├── layout.tsx # Main layout (with header)
|
|
135
|
+
│ ├── dashboard/
|
|
136
|
+
│ └── settings/
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Parallel Routes
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
// app/layout.tsx
|
|
143
|
+
export default function Layout({
|
|
144
|
+
children,
|
|
145
|
+
modal,
|
|
146
|
+
}: {
|
|
147
|
+
children: React.ReactNode;
|
|
148
|
+
modal: React.ReactNode;
|
|
149
|
+
}) {
|
|
150
|
+
return (
|
|
151
|
+
<>
|
|
152
|
+
{children}
|
|
153
|
+
{modal}
|
|
154
|
+
</>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// app/@modal/login/page.tsx
|
|
159
|
+
export default function LoginModal() {
|
|
160
|
+
return <Modal><LoginForm /></Modal>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// app/@modal/default.tsx
|
|
164
|
+
export default function Default() {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Intercepting Routes
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
app/
|
|
173
|
+
├── feed/
|
|
174
|
+
│ └── page.tsx
|
|
175
|
+
├── photo/
|
|
176
|
+
│ └── [id]/
|
|
177
|
+
│ └── page.tsx # Full page view
|
|
178
|
+
├── @modal/
|
|
179
|
+
│ └── (.)photo/
|
|
180
|
+
│ └── [id]/
|
|
181
|
+
│ └── page.tsx # Intercepted modal view
|
|
182
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Next.js Architecture (FSD)
|
|
3
|
+
description: Scalable project structure using Feature-Sliced Design (FSD).
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [nextjs, architecture, fsd, folder-structure]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['src/features/**', 'src/entities/**', 'src/widgets/**']
|
|
8
|
+
keywords: [FSD, Feature Sliced Design, slices, segments]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Architecture (Feature-Sliced Design)
|
|
12
|
+
|
|
13
|
+
## **Priority: P2 (MEDIUM)**
|
|
14
|
+
|
|
15
|
+
Adopt **Feature-Sliced Design (FSD)** for scalable applications.
|
|
16
|
+
**Warning**: FSD introduces boilerplate. Use it only if the project is expected to grow significantly (e.g., 20+ features). For smaller projects, a simple module-based structure is preferred.
|
|
17
|
+
|
|
18
|
+
## Strategy
|
|
19
|
+
|
|
20
|
+
1. **App Layer is Thin**: The `app/` directory (App Router) is **only** for Routing.
|
|
21
|
+
- _Rule_: `page.tsx` should only import Widgets/Features. No business logic (`useEffect`, `fetch`) directly in pages.
|
|
22
|
+
2. **Slices over Types**: Group code by **Business Domain** (User, Product, Cart), not by File Type (Components, Hooks, Utils).
|
|
23
|
+
- _Bad_: `src/components/LoginForm.tsx`, `src/hooks/useLogin.ts`
|
|
24
|
+
- _Good_: `src/features/auth/login/` containing both.
|
|
25
|
+
3. **Layer Hierarchy**: Code can only import from _layers below it_.
|
|
26
|
+
- `App` -> `Widgets` -> `Features` -> `Entities` -> `Shared`.
|
|
27
|
+
4. **Avoid Excessive Entities**: Do not preemptively create Entities.
|
|
28
|
+
- _Rule_: Start logic in `Features` or `Pages`. Move to `Entities` **only** when data/logic is strictly reused across multiple differing features.
|
|
29
|
+
- _Rule_: Simple CRUD belongs in `shared/api`, not `entities`.
|
|
30
|
+
5. **Standard Segments**: Use standard segment names within slices.
|
|
31
|
+
- `ui` (Components), `model` (State/actions), `api` (Data fetching), `lib` (Helpers), `config` (Constants).
|
|
32
|
+
- _Avoid_: `components`, `hooks`, `services` as segment names.
|
|
33
|
+
|
|
34
|
+
## Structure Reference
|
|
35
|
+
|
|
36
|
+
For the specific directory layout and layer definitions, see the reference documentation.
|
|
37
|
+
|
|
38
|
+
- [**FSD Folder Structure**](references/fsd-structure.md)
|
|
39
|
+
|
|
40
|
+
## Integration with Next.js Core
|
|
41
|
+
|
|
42
|
+
- **Server Actions**: Place them in the `model/` folder of a Feature (e.g., `features/auth/model/actions.ts`).
|
|
43
|
+
- **Data Access (DAL)**: Place logic in the `model/` folder of an Entity (e.g., `entities/user/model/dal.ts`).
|
|
44
|
+
- **UI Components**: Base UI (shadcn) belongs in `shared/ui`. Feature-specific UI belongs in `features/*/ui`.
|