nesties 1.1.18 → 1.1.20

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 CHANGED
@@ -676,7 +676,163 @@ This pattern is useful when you want to reuse the same resolver logic in guards,
676
676
  - `ApiFromResolver(input: ParamResolverInput, extras?: ApiHeaderOptions | ApiQueryOptions)`
677
677
  Convenience helper to generate Swagger decorators from resolver inputs.
678
678
 
679
- ### 9. DTO Classes
679
+ ### ApiInject: Making API Contracts Explicit via Dependency Injection
680
+
681
+ In real-world Nest.js applications, many API contracts are *implicit* rather than declared directly on controllers.
682
+
683
+ Common examples include:
684
+
685
+ - Authentication tokens read from headers
686
+ - Tenant or locale identifiers propagated through services
687
+ - Feature flags or internal routing hints extracted from requests
688
+
689
+ These values are often accessed deep inside services, guards, or interceptors—far away from the controller layer where API documentation (Swagger) is usually defined.
690
+
691
+ This creates a long-standing tension:
692
+
693
+ - **Dependency Injection (DI)** knows what a controller *depends on*
694
+ - **Swagger decorators** describe what an API *expects from the client*
695
+ - But the two are traditionally maintained **separately and manually**
696
+
697
+ `@ApiInject()` exists to bridge this gap.
698
+
699
+ ---
700
+
701
+ #### What `@ApiInject()` Does
702
+
703
+ `@ApiInject()` is an explicit opt-in decorator that behaves like `@Inject()`, but additionally allows Nesties to **infer API contract metadata from the dependency graph** and attach the corresponding Swagger documentation automatically.
704
+
705
+ In other words:
706
+
707
+ > If a controller explicitly injects something that depends on request parameters,
708
+ > then those parameters are part of the API contract and should be documented.
709
+
710
+ ---
711
+
712
+ #### Why This Is Explicit (Not Magic)
713
+
714
+ A key design principle of Nesties is that **API contracts should never be inferred silently**.
715
+
716
+ `@ApiInject()` is intentionally *not* a drop-in replacement for `@Inject()`.
717
+
718
+ By choosing to write:
719
+
720
+ ```ts
721
+ @ApiInject()
722
+ private readonly articleService: ArticleService;
723
+ ```
724
+
725
+ you are explicitly stating:
726
+
727
+ > “This dependency participates in the public API contract of this controller.”
728
+
729
+ Only dependencies injected via `@ApiInject()` are eligible for automatic Swagger inference.
730
+ Regular `@Inject()` remains side-effect free.
731
+
732
+ This makes the behavior:
733
+ - Predictable
734
+ - Auditable
735
+ - Easy to reason about in code review
736
+
737
+ ---
738
+
739
+ #### How Swagger Metadata Is Inferred
740
+
741
+ `@ApiInject()` works in combination with `ParamResolver` and request-scoped providers.
742
+
743
+ At a high level:
744
+
745
+ 1. `ParamResolver` declares **how** a value is resolved from a request (header, query, or dynamic logic)
746
+ 2. `toRequestScopedProvider()` turns that resolver into an injectable token
747
+ 3. Services depend on those tokens through standard Nest.js DI
748
+ 4. `@ApiInject()` walks the dependency graph starting from the injected token
749
+ 5. Any Swagger metadata declared by resolvers is automatically applied to the controller
750
+
751
+ This ensures that:
752
+
753
+ - Swagger documentation reflects *actual runtime requirements*
754
+ - API headers and query parameters are documented once, at their source
755
+ - Changes to resolver logic propagate consistently across all consumers
756
+
757
+ ---
758
+
759
+ #### Example: Documenting Headers via Dependency Injection
760
+
761
+ ```ts
762
+ const userTokenResolver = new ParamResolver({
763
+ paramType: 'header',
764
+ paramName: 'x-user-token',
765
+ });
766
+
767
+ const userTokenProvider = userTokenResolver.toRequestScopedProvider();
768
+
769
+ @Injectable()
770
+ class ArticleService {
771
+ constructor(
772
+ @userTokenProvider.inject()
773
+ private readonly userToken: string | undefined,
774
+ ) {}
775
+ }
776
+
777
+ @Controller('articles')
778
+ export class ArticleController {
779
+ constructor(
780
+ @ApiInject()
781
+ private readonly articleService: ArticleService,
782
+ ) {}
783
+
784
+ @Get()
785
+ list() {
786
+ // ...
787
+ }
788
+ }
789
+ ```
790
+
791
+ In this example:
792
+
793
+ - `ArticleService` depends on `x-user-token`
794
+ - The controller explicitly opts into API inference via `@ApiInject()`
795
+ - `x-user-token` is automatically documented in Swagger
796
+ - No manual `@ApiHeader()` is required on the controller
797
+
798
+ ---
799
+
800
+ #### Design Philosophy
801
+
802
+ `@ApiInject()` is based on a simple idea:
803
+
804
+ > **Dependency Injection already encodes API contracts — we should not ignore that information.**
805
+
806
+ Rather than duplicating knowledge across:
807
+ - controllers (Swagger decorators)
808
+ - services (request access logic)
809
+ - guards and interceptors
810
+
811
+ Nesties treats DI as the single source of truth and lets Swagger documentation follow from it.
812
+
813
+ This approach improves:
814
+ - Consistency between implementation and documentation
815
+ - Maintainability in large codebases
816
+ - Confidence that documented APIs match actual runtime behavior
817
+
818
+ ---
819
+
820
+ #### When to Use (and Not Use) `@ApiInject()`
821
+
822
+ Use `@ApiInject()` when:
823
+ - A dependency influences how requests are interpreted
824
+ - A resolver reads from headers or query parameters
825
+ - The dependency represents part of the public API contract
826
+
827
+ Avoid `@ApiInject()` when:
828
+ - The dependency is purely internal
829
+ - It does not depend on request data
830
+ - You do not want it reflected in Swagger documentation
831
+
832
+ Keeping this distinction explicit is what allows Nesties to scale safely in large teams.
833
+
834
+
835
+ ### 10. DTO Classes
680
836
 
681
837
  - **BlankReturnMessageDto**: A basic DTO for standardized API responses.
682
838
  - **BlankPaginatedReturnMessageDto**: A DTO for paginated API responses.