<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Костя Третяк</title>
    <description>The latest articles on DEV Community by Костя Третяк (@kostyatretyak).</description>
    <link>https://dev.to/kostyatretyak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F398023%2F5deaa82a-162c-4f3e-a075-99469a954954.jpg</url>
      <title>DEV Community: Костя Третяк</title>
      <link>https://dev.to/kostyatretyak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kostyatretyak"/>
    <language>en</language>
    <item>
      <title>Application Lifecycle and Graceful Shutdown</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sun, 26 Jul 2026 17:49:33 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-application-lifecycle-and-graceful-shutdown-part-5-2joa</link>
      <guid>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-application-lifecycle-and-graceful-shutdown-part-5-2joa</guid>
      <description>&lt;p&gt;Welcome to Part 5—the grand finale of our &lt;strong&gt;"NestJS v12.0 vs. Holu v0.1"&lt;/strong&gt; series!&lt;/p&gt;

&lt;p&gt;Over the last four articles, we have unpacked the strict Dependency Injection hierarchies, Native ESM migrations, compile-time Extension Groups, and powerful Metadata Reflection APIs of these two enterprise Node.js frameworks.&lt;/p&gt;

&lt;p&gt;But building a robust enterprise application isn't just about how it starts up or how it handles requests. In the modern cloud-native world of Kubernetes, Docker, and autoscaling groups, &lt;strong&gt;how your application shuts down is just as critical as how it runs.&lt;/strong&gt; When a container orchestrator sends a &lt;code&gt;SIGTERM&lt;/code&gt; signal, your app needs to stop accepting new requests, finish processing active ones, and safely close database connections to prevent data corruption.&lt;/p&gt;

&lt;p&gt;Today, we are exploring the &lt;strong&gt;Application Lifecycle and Graceful Shutdown&lt;/strong&gt; mechanics of NestJS and &lt;a href="http://holujs.github.io/en/" rel="noopener noreferrer"&gt;Holu&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. NestJS: The Classic Lifecycle Hooks
&lt;/h2&gt;

&lt;p&gt;NestJS provides a robust, well-documented lifecycle interface. To handle startup, you use &lt;code&gt;OnModuleInit&lt;/code&gt; and &lt;code&gt;OnApplicationBootstrap&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For graceful shutdown, NestJS requires you to explicitly opt-in by calling &lt;code&gt;app.enableShutdownHooks()&lt;/code&gt; in your &lt;code&gt;main.ts&lt;/code&gt;. Once enabled, NestJS listens for system signals (like &lt;code&gt;SIGINT&lt;/code&gt; or &lt;code&gt;SIGTERM&lt;/code&gt;) and executes three shutdown phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;OnModuleDestroy&lt;/code&gt;&lt;/strong&gt;: Called after the shutdown signal is received.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BeforeApplicationShutdown&lt;/code&gt;&lt;/strong&gt;: Called before connections are closed (useful for draining custom event loops).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;OnApplicationShutdown&lt;/code&gt;&lt;/strong&gt;: The final hook where you close database pools or Redis clients.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NestJS traverses the DI container and executes these hooks for all registered providers. However, developers must be careful: if a shutdown hook throws an unhandled error, or if you don't properly manage in-flight HTTP connections, the process might hang or drop requests abruptly.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Holu v0.1: The Three-Step Shutdown Sequence
&lt;/h2&gt;

&lt;p&gt;Holu also requires you to activate signal interception explicitly by calling &lt;code&gt;app.enableShutdownHooks()&lt;/code&gt; on the application instance. By default, it intercepts &lt;code&gt;SIGTERM&lt;/code&gt;, &lt;code&gt;SIGINT&lt;/code&gt;, &lt;code&gt;SIGHUP&lt;/code&gt;, &lt;code&gt;SIGUSR2&lt;/code&gt;, and &lt;code&gt;SIGQUIT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;app.close(signal)&lt;/code&gt; is triggered, Holu’s &lt;code&gt;BaseApplication&lt;/code&gt; executes a strictly defined 3-step sequence:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: &lt;code&gt;BeforeShutdown&lt;/code&gt; Hooks
&lt;/h3&gt;

&lt;p&gt;Holu first invokes &lt;code&gt;beforeShutdown()&lt;/code&gt; on all relevant singleton services. This phase is ideal for stopping background timers or queue workers. For instance, Holu's &lt;code&gt;@holu/schedule&lt;/code&gt; extension automatically implements this hook to clear pending timers, stop cron jobs, and delete intervals before the server even stops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: &lt;code&gt;customShutdown(signal)&lt;/code&gt; (Connection Draining)
&lt;/h3&gt;

&lt;p&gt;This is an extension point method in &lt;code&gt;BaseApplication&lt;/code&gt;. In a REST environment, &lt;code&gt;RestApplication&lt;/code&gt; overrides this method to perform package-level shutdown logic.&lt;br&gt;
During this phase, the HTTP server initiates closure (&lt;code&gt;server.close()&lt;/code&gt;). It stops receiving new TCP connections and immediately destroys idle keep-alive connections. Active, in-flight HTTP requests are allowed to finish processing, waiting up to the configured &lt;code&gt;shutdownTimeout&lt;/code&gt; (which defaults to 15,000 ms).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: &lt;code&gt;OnShutdown&lt;/code&gt; Hooks
&lt;/h3&gt;

&lt;p&gt;Once the HTTP server is completely closed and active requests have drained, Holu calls the &lt;code&gt;onShutdown()&lt;/code&gt; hook on your services. This is the safest place to close database connection pools or file handles, knowing that no incoming HTTP requests will attempt to use the database during the closure.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Holu Performance Nuances
&lt;/h2&gt;

&lt;p&gt;While the hooks might look similar to NestJS on the surface, Holu implements several aggressive performance and safety optimizations under the hood:&lt;/p&gt;

&lt;h3&gt;
  
  
  Instantiated Singletons Only
&lt;/h3&gt;

&lt;p&gt;In NestJS, the lifecycle system generally processes the entire provider tree.&lt;br&gt;
Holu is ruthlessly efficient: shutdown hooks are invoked &lt;strong&gt;only on singleton instances (&lt;code&gt;providersPerApp&lt;/code&gt; and &lt;code&gt;providersPerMod&lt;/code&gt;) that were actually instantiated&lt;/strong&gt; during application runtime. Uninstantiated services, or any request-scoped and route-scoped providers (&lt;code&gt;providersPerReq&lt;/code&gt;, &lt;code&gt;providersPerRou&lt;/code&gt;), are completely excluded from shutdown hooks. This saves execution time and prevents the framework from spinning up idle dependencies just to shut them down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrent Execution &amp;amp; Fault Tolerance
&lt;/h3&gt;

&lt;p&gt;When shutting down a large app, you don't want a single failing Redis connection to prevent your Postgres pool from closing gracefully.&lt;br&gt;
Holu executes hooks for all active instances concurrently using &lt;code&gt;Promise.allSettled()&lt;/code&gt;. Errors thrown in individual &lt;code&gt;beforeShutdown&lt;/code&gt; or &lt;code&gt;onShutdown&lt;/code&gt; hooks are safely caught, logged via &lt;code&gt;SystemLogMediator&lt;/code&gt;, and do not interrupt other services' shutdown hooks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion of the Series
&lt;/h2&gt;

&lt;p&gt;As we conclude this 5-part deep dive into &lt;strong&gt;NestJS v12.0&lt;/strong&gt; and &lt;strong&gt;Holu v0.1&lt;/strong&gt;, the philosophical differences between the two frameworks are crystal clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NestJS v12.0&lt;/strong&gt; remains the undisputed king of ecosystem maturity and developer convenience. With its full Native ESM migration, standard schema (Zod/Valibot) routing support, and vast array of off-the-shelf modules, it is the safest bet for most teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Holu v0.1&lt;/strong&gt; is an architectural purist’s dream. By enforcing an incredibly strict Dependency Injection hierarchy, eliminating silent collisions, validating parameters via core &lt;code&gt;FactoryProviders&lt;/code&gt;, merging metadata via a deeply integrated &lt;code&gt;Reflector&lt;/code&gt; (with &lt;code&gt;ParentParams&lt;/code&gt; inheritance!), and compiling infrastructure via three-stage Extension Groups, Holu removes "framework magic" and replaces it with predictable, explicit engineering.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both frameworks are pushing the boundaries of what is possible in enterprise Node.js.&lt;/p&gt;

&lt;p&gt;Which one will you choose for your next project? Drop a comment below, and happy coding!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>holu</category>
    </item>
    <item>
      <title>Reflection APIs, Metadata, and Custom Decorators</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:29:39 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-reflection-apis-metadata-and-custom-decorators-part-4-4baf</link>
      <guid>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-reflection-apis-metadata-and-custom-decorators-part-4-4baf</guid>
      <description>&lt;p&gt;Welcome to Part 4 of the &lt;strong&gt;"NestJS v12.0 vs. Holu v0.1"&lt;/strong&gt; series! In Part 3, we explored how both frameworks handle extensibility through Dynamic Modules and Extension Groups.&lt;/p&gt;

&lt;p&gt;Today, we are looking at the invisible glue that holds both of these Node.js frameworks together: &lt;strong&gt;TypeScript Decorators and Metadata Reflection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you use NestJS or Holu, your code is covered in decorators—&lt;code&gt;@Controller()&lt;/code&gt;, &lt;code&gt;@Injectable()&lt;/code&gt;, &lt;code&gt;@Get()&lt;/code&gt;, etc. But what happens when you need to write your &lt;em&gt;own&lt;/em&gt; custom decorators? How do these frameworks read, store, and merge that metadata under the hood?&lt;/p&gt;

&lt;p&gt;Let's compare the traditional NestJS &lt;code&gt;@SetMetadata&lt;/code&gt; approach with &lt;a href="https://holujs.github.io/en/" rel="noopener noreferrer"&gt;Holu&lt;/a&gt;'s highly engineered &lt;code&gt;Reflector&lt;/code&gt; abstraction.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. NestJS: The &lt;code&gt;@SetMetadata&lt;/code&gt; Approach
&lt;/h2&gt;

&lt;p&gt;NestJS provides a straightforward, accessible API for dealing with custom metadata, built heavily around the standard &lt;code&gt;reflect-metadata&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;When you want to attach custom data to a route (for example, marking a route as "public" or assigning specific roles), you use &lt;code&gt;@SetMetadata()&lt;/code&gt; or create a custom decorator factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SetMetadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;SetMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;roles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;super-admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getDashboard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To read this metadata later (usually inside a Guard or Interceptor), you inject the NestJS &lt;code&gt;Reflector&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CanActivate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ExecutionContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RolesGuard&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;CanActivate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;reflector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;canActivate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExecutionContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reflector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;roles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHandler&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchToHttp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Limitations:&lt;/strong&gt;&lt;br&gt;
This approach is simple and effective. However, it relies on basic key-value retrieval. If you have a complex OOP structure where a child controller extends a base CRUD controller, &lt;code&gt;reflect-metadata&lt;/code&gt; (and by extension, the default NestJS reflector methods) does not automatically traverse the inheritance tree to merge metadata from the parent class down to the child. Developers often have to write manual fallback logic to check the prototype chain.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Holu v0.1: The Unified Reflector Abstraction
&lt;/h2&gt;

&lt;p&gt;Holu takes a fundamentally different, heavily optimized approach to metadata. Instead of just wrapping &lt;code&gt;reflect-metadata&lt;/code&gt;, Holu's &lt;code&gt;Reflector&lt;/code&gt; provides a unified, cached, and high-level abstraction over standard reflection metadata. (It uses the lighter &lt;code&gt;reflect-metadata/lite&lt;/code&gt; polyfill under the hood, which is loaded automatically by &lt;code&gt;@holu/core&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating Type-Safe Decorators
&lt;/h3&gt;

&lt;p&gt;Instead of raw key-value pairs, Holu provides static factory methods to generate structured decorators: &lt;code&gt;Reflector.makeClassDecorator()&lt;/code&gt;, &lt;code&gt;Reflector.makePropDecorator()&lt;/code&gt;, and &lt;code&gt;Reflector.makeParamDecorator()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can pass a transformer function directly into the factory to validate and format the arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RoleOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Generates a property-level decorator and transforms its arguments&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requireRoles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makePropDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;strict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;strict&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;RoleOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;requireRoles&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;super-admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;secureData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inheritance Traversal (The Killer Feature)
&lt;/h3&gt;

&lt;p&gt;One of the most powerful features of Holu's &lt;code&gt;Reflector&lt;/code&gt; is its native understanding of OOP inheritance. It automatically merges class, property, and parameter metadata from parent classes down to children.&lt;/p&gt;

&lt;p&gt;When Holu collects metadata for a class, it builds an internal mapping of the inheritance tree, tracking exactly where each decorator originated via the &lt;code&gt;decoratorChain&lt;/code&gt; and &lt;code&gt;paramChain&lt;/code&gt; fields of the &lt;code&gt;MergedClassPropMeta&lt;/code&gt; object. If you extend a base controller, the child seamlessly inherits the parent's route and parameter decorators without any manual prototype hacking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Constructor Inheritance: The &lt;code&gt;ParentParams&lt;/code&gt; Token
&lt;/h3&gt;

&lt;p&gt;One of the most frustrating aspects of standard TypeScript DI (including NestJS) is constructor inheritance. If your &lt;code&gt;ChildController&lt;/code&gt; extends a &lt;code&gt;BaseCrudController&lt;/code&gt; that has five injected services, your &lt;code&gt;ChildController&lt;/code&gt; must explicitly declare all five of those services in its constructor just to pass them up via &lt;code&gt;super(dep1, dep2, ...)&lt;/code&gt;. If the base class changes, every child class breaks.&lt;/p&gt;

&lt;p&gt;Because Holu's &lt;code&gt;Reflector&lt;/code&gt; natively understands inheritance chains, its Dependency Injection system can solve this automatically using the special &lt;code&gt;ParentParams&lt;/code&gt; token.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;@injectable()&lt;/code&gt; class extends a parent class, Holu automatically gathers the parent's required dependencies and injects them as an array into &lt;code&gt;ParentParams&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ParentParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/core/di&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DbService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChildController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// We inject the parent's parameters cleanly without redeclaring them!&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ParentParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;parentParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ConstructorParameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;BaseController&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;childSpecificService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChildSpecificService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Spread the array into super()&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;parentParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;code&gt;@inject(ParentParams)&lt;/code&gt; decorator for strict type safety, or use the &lt;code&gt;@ts-expect-error&lt;/code&gt; shorthand approach. Either way, Holu completely eliminates the brittle boilerplate of passing dependencies up the prototype chain!&lt;/p&gt;

&lt;h3&gt;
  
  
  Decorator Grouping (&lt;code&gt;decoratorId&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;When building complex plugins, you often have multiple decorators that belong to the same logical family. Holu allows you to group them effortlessly.&lt;/p&gt;

&lt;p&gt;When creating a decorator, you can pass a reference to a "base" decorator as the third argument (the &lt;code&gt;decoratorId&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DecoratorMeta&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Create a base decorator (acts as the Group ID)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeClassDecorator&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiGroup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Create related decorators, passing `apiGroup` as the decoratorId&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeClassDecorator&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiModel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Reflector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeClassDecorator&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apiEntity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later, you can extract all metadata associated with that specific group identifier, making it incredibly easy to parse large, multi-decorator structures for things like OpenAPI documentation generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iterability and Caching
&lt;/h3&gt;

&lt;p&gt;When you need to read an entire class at once, Holu provides &lt;code&gt;Reflector.collectMeta(Cls)&lt;/code&gt;. This returns a &lt;code&gt;MergedClassMeta&lt;/code&gt; object which is fully iterable. You can loop through it to get property-by-property details, constructor parameters, and inheritance maps, all of which are resolved once and cached internally for maximum performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Both frameworks provide excellent tools for metadata reflection, but they are built for slightly different developer experiences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NestJS v12.0&lt;/strong&gt; maintains its highly approachable &lt;code&gt;@SetMetadata&lt;/code&gt; and &lt;code&gt;Reflector.get()&lt;/code&gt; pattern. It is incredibly easy to learn and covers 95% of standard web application use cases perfectly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Holu v0.1&lt;/strong&gt; treats metadata reflection as a core infrastructure pillar. By providing automated inheritance traversal, decorator grouping via &lt;code&gt;decoratorId&lt;/code&gt;, and a strongly typed, iterable &lt;code&gt;MergedClassMeta&lt;/code&gt; object, Holu completely eliminates the boilerplate required to parse complex OOP structures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building simple guards, NestJS's approach is quick and painless. But if you are building complex libraries, automated code generators, or heavily abstracted base classes, Holu’s Reflection API feels like a superpower.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 5&lt;/strong&gt; of the series, we will wrap up by exploring &lt;strong&gt;Application Lifecycle and Graceful Shutdown&lt;/strong&gt; — comparing how both frameworks safely drain HTTP connections and close database pools in production environments. Stay tuned!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>holu</category>
    </item>
    <item>
      <title>Dynamic Modules, Providers, and the Power of Extension Groups</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:00:26 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-dynamic-modules-providers-and-the-power-of-extension-groups-part-1jeh</link>
      <guid>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-dynamic-modules-providers-and-the-power-of-extension-groups-part-1jeh</guid>
      <description>&lt;p&gt;Welcome to Part 3 of the &lt;strong&gt;"NestJS v12.0 vs. Holu v0.1"&lt;/strong&gt; series! In our previous articles, we explored the strict Dependency Injection hierarchies and modern validation pipelines of these two cutting-edge Node.js frameworks. &lt;/p&gt;

&lt;p&gt;Today, we are tackling the true hallmark of an enterprise framework: &lt;strong&gt;Extensibility&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When building large-scale applications, you inevitably need to write reusable libraries, configurable plugins, and complex infrastructure hooks (like OpenAPI generators, custom routers, or database orchestrators). Both NestJS and Holu draw heavy inspiration from Angular's architectural playbook to solve this, but their execution diverges dramatically as application complexity scales.&lt;/p&gt;

&lt;p&gt;Let's explore how Dynamic Modules, Dynamic Providers, and &lt;a href="http://holujs.github.io/en/" rel="noopener noreferrer"&gt;Holu&lt;/a&gt;’s unique Extension Groups compare.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Shared Ancestry: Dynamic Modules &amp;amp; Dynamic Providers
&lt;/h2&gt;

&lt;p&gt;If you have ever used Angular, you are likely familiar with the &lt;code&gt;RouterModule.forRoot()&lt;/code&gt; pattern. This is a &lt;strong&gt;Dynamic Module&lt;/strong&gt;—a module that doesn't just statically register providers, but accepts configuration at runtime to dynamically generate providers before the DI container is built.&lt;/p&gt;

&lt;h3&gt;
  
  
  NestJS Dynamic Modules
&lt;/h3&gt;

&lt;p&gt;NestJS fully embraces this concept. You return a &lt;code&gt;DynamicModule&lt;/code&gt; object containing &lt;code&gt;providers&lt;/code&gt;, &lt;code&gt;exports&lt;/code&gt;, and &lt;code&gt;module&lt;/code&gt; from a static factory method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DynamicModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DynamicModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DatabaseModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CONNECTION_STRING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Holu Dynamic Modules
&lt;/h3&gt;

&lt;p&gt;Holu provides the exact same architectural capability, rooted in the same Angular philosophy. Holu supports importing modules in two forms: Static Modules (a raw class reference) and Dynamic Modules (an object implementing the &lt;code&gt;DynamicModule&lt;/code&gt; interface).&lt;/p&gt;

&lt;p&gt;Using Dynamic Modules in Holu, configuration details, route prefixes, or custom provider overrides can be supplied directly at the import site.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DynamicModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SomeService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./some.service.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// A static helper method returning a DynamicModule&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;withConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DynamicModule&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UsersModule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;providersPerReq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CUSTOM_CONFIG&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; 
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both frameworks, Dynamic Modules are excellent for initializing dynamic providers (like injecting a specific API key or database configuration).&lt;/p&gt;

&lt;p&gt;However, what happens when you need to do something more complex? What if you need to scan the entire application for specific decorators, dynamically generate routes, or aggregate metadata from ten different third-party modules to build a Swagger file?&lt;/p&gt;

&lt;h2&gt;
  
  
  NestJS: Lifecycle Hooks &amp;amp; The Discovery Service
&lt;/h2&gt;

&lt;p&gt;In NestJS, complex framework extensions are typically handled using a combination of &lt;strong&gt;Lifecycle Hooks&lt;/strong&gt; (&lt;code&gt;OnModuleInit&lt;/code&gt;, &lt;code&gt;OnApplicationBootstrap&lt;/code&gt;) and the &lt;strong&gt;DiscoveryService&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you are building an OpenAPI generator in NestJS, your logic lives inside an &lt;code&gt;OnApplicationBootstrap&lt;/code&gt; hook. You inject the &lt;code&gt;DiscoveryService&lt;/code&gt;, iterate over every registered controller and provider, read their Reflect metadata using the &lt;code&gt;Reflector&lt;/code&gt;, and dynamically build your schema.&lt;/p&gt;

&lt;p&gt;While this approach works, it has limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It occurs &lt;em&gt;at runtime&lt;/em&gt; (after the DI container is fully instantiated).&lt;/li&gt;
&lt;li&gt;You cannot easily inject new providers into the application based on what you discover.&lt;/li&gt;
&lt;li&gt;Coordination between multiple independent plugins requires complex workarounds.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Holu v0.1: The Extension System &amp;amp; Groups
&lt;/h2&gt;

&lt;p&gt;Holu abandons runtime lifecycle discovery for infrastructure setup. Instead, it introduces a highly structured &lt;strong&gt;Extension System&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Extensions operate strictly during the application initialization stage to set up infrastructure and do not participate directly in request processing. They run &lt;em&gt;after&lt;/em&gt; static metadata is collected from decorators, but &lt;em&gt;before&lt;/em&gt; request handlers and DI containers are fully finalized.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Three-Stage Pipeline
&lt;/h3&gt;

&lt;p&gt;Every Holu Extension can implement up to three sequential framework lifecycle hooks that are always executed in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;stage1(isLastModule)&lt;/code&gt;&lt;/strong&gt;: Used to dynamically collect metadata, push new providers into the &lt;code&gt;providersPerApp&lt;/code&gt;, &lt;code&gt;providersPerMod&lt;/code&gt;, &lt;code&gt;providersPerRou&lt;/code&gt;, or &lt;code&gt;providersPerReq&lt;/code&gt; arrays, and aggregate data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;stage2(injectorPerMod)&lt;/code&gt;&lt;/strong&gt;: Called after &lt;code&gt;stage1&lt;/code&gt; finishes for ALL modules, receiving a fully prepared module-level injector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;stage3()&lt;/code&gt;&lt;/strong&gt;: Used for final post-processing before the application starts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because &lt;code&gt;stage1&lt;/code&gt; executes while provider arrays are still being assembled, a Holu extension can read a module's configuration and dynamically push an interceptor directly into that specific route's &lt;code&gt;providersPerReq&lt;/code&gt; array. You are modifying the application graph &lt;em&gt;before&lt;/em&gt; it is sealed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extension Groups: The True Game Changer
&lt;/h3&gt;

&lt;p&gt;The most powerful feature of Holu's extensibility is &lt;strong&gt;Extension Groups&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An extension group allows multiple independent extensions to contribute data under the class token of a "Lead Extension". For example, if you have a core &lt;code&gt;RestRouteExtension&lt;/code&gt; that handles routing, other third-party extensions can hook into it simply by declaring it in their &lt;code&gt;groups&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. A third-party OpenAPI extension joins the RestRouteExtension group&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OpenApiExtensionConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OpenApiRouteExtension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RestRouteExtension&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Joins the group seamlessly&lt;/span&gt;
  &lt;span class="na"&gt;export&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an extension joins a group, it automatically inherits the group's positioning relative to other extensions in the execution graph.&lt;/p&gt;

&lt;p&gt;Later, when the some extension asks the &lt;code&gt;ExtensionManager&lt;/code&gt; for its &lt;code&gt;stage1&lt;/code&gt; data, the manager returns an aggregated payload containing data from the Lead Extension &lt;em&gt;and&lt;/em&gt; all member extensions registered in its group.&lt;/p&gt;

&lt;p&gt;This means extensions can share a base interface contract and collaboratively build infrastructure (like appending Swagger documentation metadata to core route metadata) without being tightly coupled or requiring a global &lt;code&gt;DiscoveryService&lt;/code&gt; hack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Both NestJS and Holu provide excellent tools for basic configurability via Angular-inspired &lt;strong&gt;Dynamic Modules&lt;/strong&gt; and &lt;strong&gt;Dynamic Providers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, when building complex architectural plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NestJS&lt;/strong&gt; relies on runtime scanning via &lt;code&gt;DiscoveryService&lt;/code&gt; and lifecycle hooks, which can be limiting if you need to mutate the DI container dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Holu&lt;/strong&gt; solves this with a dedicated compile-time-like &lt;strong&gt;Extension System&lt;/strong&gt;. By utilizing &lt;code&gt;stage1&lt;/code&gt; hooks and &lt;strong&gt;Extension Groups&lt;/strong&gt;, Holu allows plugins to collaboratively aggregate metadata and dynamically push providers into the DI hierarchy &lt;em&gt;before&lt;/em&gt; the application fully starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 4 of the series, we will take a deep dive into &lt;strong&gt;Reflection APIs and Custom Decorators&lt;/strong&gt;, exploring how both frameworks handle metadata merging and inheritance. Stay tuned!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>angular</category>
      <category>holu</category>
    </item>
    <item>
      <title>ESM Migration, Validation, and Request Execution</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sun, 26 Jul 2026 14:30:21 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-esm-migration-validation-and-request-execution-part-2-2gab</link>
      <guid>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-esm-migration-validation-and-request-execution-part-2-2gab</guid>
      <description>&lt;p&gt;Welcome to Part 2 of the &lt;strong&gt;"NestJS v12.0 vs. Holu v0.1"&lt;/strong&gt; series! If you missed Part 1, we covered the foundational differences in how these two frameworks handle Modules, Dependency Injection (DI) hierarchies, and Application Extensions.&lt;/p&gt;

&lt;p&gt;Right now, both frameworks are in active development for their next major leaps and can be downloaded from npm using the &lt;code&gt;next&lt;/code&gt; tag. This is an exciting time for Node.js developers because the ecosystem is undergoing a massive architectural shift. &lt;/p&gt;

&lt;p&gt;In this article, we will dive into the code. We'll explore how NestJS v12.0 is modernizing its stack with native ESM and Standard Schema support, and compare that with &lt;a href="https://holujs.github.io/en/" rel="noopener noreferrer"&gt;Holu v0.1&lt;/a&gt;’s highly strict, DI-integrated approach to parameter validation and route interception.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Native ESM Era Has Arrived
&lt;/h2&gt;

&lt;p&gt;For years, developers have been trapped in the CommonJS (CJS) vs ECMAScript Modules (ESM) limbo. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NestJS v12.0&lt;/strong&gt; is finally slamming the door on that friction. The upcoming v12 release brings a massive &lt;strong&gt;full migration to Native ESM&lt;/strong&gt; across all official packages. Thanks to Node.js's new &lt;code&gt;require(esm)&lt;/code&gt; support, the transition is smoother than ever. &lt;/p&gt;

&lt;p&gt;But the NestJS team didn't stop there. They are completely modernizing the default toolchain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt; replaces Jest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;oxlint&lt;/strong&gt; replaces ESLint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rspack&lt;/strong&gt; replaces Webpack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Holu v0.1&lt;/strong&gt;, on the other hand, was built with modern ECMAScript standards and top-level await in mind from the beginning. Holu applications natively leverage ESM for bootstrapping (like &lt;code&gt;await RestApplication.create(AppModule)&lt;/code&gt;). When it comes to testing, Holu easily integrates with Vitest, requiring only that &lt;code&gt;reflect-metadata/lite&lt;/code&gt; is loaded early in the test setup. &lt;/p&gt;

&lt;p&gt;Both frameworks now ensure that your entire stack—from frontend to backend in a monorepo—can finally run seamlessly on a single, native module language without redundant compilation layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Parameter Validation: Standard Schema vs. Factory Providers
&lt;/h2&gt;

&lt;p&gt;When a request comes in, you need to validate the payload and extract parameters. Here, the two frameworks take wildly different philosophies.&lt;/p&gt;

&lt;h3&gt;
  
  
  NestJS v12.0: Standard Schema Integration
&lt;/h3&gt;

&lt;p&gt;NestJS 12 introduces a highly anticipated feature: &lt;strong&gt;native Standard Schema support&lt;/strong&gt;. You no longer need to rely heavily on the decorator-heavy &lt;code&gt;class-validator&lt;/code&gt; and &lt;code&gt;class-transformer&lt;/code&gt; packages.&lt;/p&gt;

&lt;p&gt;Instead, NestJS v12 allows you to pass schemas from modern validation libraries like &lt;strong&gt;Zod, Valibot, or ArkType&lt;/strong&gt; directly into your route decorators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createUserSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;createUserSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`User &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; created!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces metadata overhead and gives developers immediate access to the rich type inference of modern schema libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Holu v0.1: Parameter Validation via DI Pipes
&lt;/h3&gt;

&lt;p&gt;Holu does not use separate conceptual entities like "Pipes" for validation. Instead, it handles parameter validation and transformation strictly through its Dependency Injection System, using custom &lt;code&gt;FactoryProvider&lt;/code&gt; logic.&lt;/p&gt;

&lt;p&gt;When you inject a parameter in Holu, you can pass contextual data directly to the injection token via the second argument of &lt;code&gt;@inject(Dependency, 'input-data')&lt;/code&gt;. The DI injector skips caching for this dependency and executes the factory method afresh for each injection site, passing that second argument to the &lt;code&gt;@input&lt;/code&gt; decorator inside the factory.&lt;/p&gt;

&lt;p&gt;Here is how you build an equivalent Zod validation pipe for the request body in Holu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;factoryMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AnyObj&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/rest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HTTP_BODY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Create a generic Zod Factory Provider&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ZodBodyPipe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;factoryMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HTTP_BODY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnyObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZodSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Assuming the body is parsed by @holu/body-parser and attached to body&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// In a real app, you would throw a specific HTTP error (e.g., BadRequestError)&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Validation failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Inject it into the Controller Method&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// We pass the Zod schema as the second argument to @inject.&lt;/span&gt;
    &lt;span class="c1"&gt;// Holu dynamically feeds it into the @input parameter of ZodBodyPipe!&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ZodBodyPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createUserSchema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;createUserSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`User &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; created!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The difference:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the NestJS ecosystem, native Standard Schema support is a major framework-level feature that developers have been waiting for the core team to ship in v12.&lt;/p&gt;

&lt;p&gt;Holu, on the other hand, shifts this power directly to the developer &lt;em&gt;right now&lt;/em&gt;. Because Holu handles validation entirely through its standard Dependency Injection system via &lt;a href="https://holujs.github.io/en/basic-components/dependency-injection/#provider" rel="noopener noreferrer"&gt;FactoryProvider mechanics&lt;/a&gt;, you don't need to wait for official framework integrations or special decorators to support new schema libraries. You have the flexibility to easily write these custom, deeply integrated pipes yourself using the core DI tools already provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Interceptors and Request Execution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  NestJS Interceptors
&lt;/h3&gt;

&lt;p&gt;In NestJS, the lifecycle is broadly: &lt;code&gt;Middleware -&amp;gt; Guards -&amp;gt; Interceptors -&amp;gt; Pipes -&amp;gt; Controllers&lt;/code&gt;.&lt;br&gt;
You bind an interceptor using the &lt;code&gt;@UseInterceptors()&lt;/code&gt; decorator. Inside, you manipulate the RxJS stream using &lt;code&gt;next.handle()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Holu's Strict RequestDispatcher Pipeline
&lt;/h3&gt;

&lt;p&gt;Holu (&lt;code&gt;@holu/rest&lt;/code&gt;) has a strict, highly optimized, non-RxJS interceptor chain managed by the &lt;code&gt;RequestDispatcherExtension&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpFrontend&lt;/code&gt;&lt;/strong&gt;: Parses path/query parameters into &lt;code&gt;RequestContext&lt;/code&gt; and formats the final response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GuardedInterceptor&lt;/code&gt;&lt;/strong&gt;: Executes &lt;code&gt;CanActivate&lt;/code&gt; guards. (If a guard fails, execution stops and throws a CustomError).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom `HTTP_INTERCEPTORS&lt;/strong&gt;`: Your custom logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpBackend&lt;/code&gt;&lt;/strong&gt;: Invokes the controller method.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Adding Route-Level Interceptors in Holu:&lt;/strong&gt;&lt;br&gt;
Instead of a separate decorator, Holu allows you to pass an array of interceptors directly into the 4th argument of the &lt;code&gt;@route()&lt;/code&gt; decorator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpInterceptor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RequestContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@holu/rest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingInterceptor&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;HttpInterceptor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Signature: @route(method, path, guards, interceptors)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard/stats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LoggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; 
  &lt;span class="nf"&gt;getStats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;visitors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During bootstrap (&lt;code&gt;stage1&lt;/code&gt;), the Holu &lt;code&gt;InterceptorExtension&lt;/code&gt; automatically extracts these interceptors and registers them into the &lt;code&gt;HTTP_INTERCEPTORS&lt;/code&gt; multi-provider array at the &lt;code&gt;providersPerRou&lt;/code&gt; or &lt;code&gt;providersPerReq&lt;/code&gt; level.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Crucial Note: If you need to trace or wrap the ENTIRE request lifecycle—including Guard execution—in Holu, you don't use standard interceptors. You override the entire &lt;code&gt;RequestDispatcher&lt;/code&gt; at the &lt;code&gt;providersPerApp&lt;/code&gt; level).&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Both &lt;strong&gt;NestJS v12.0&lt;/strong&gt; and &lt;strong&gt;Holu v0.1&lt;/strong&gt; (available now under the &lt;code&gt;next&lt;/code&gt; npm tags) represent the bleeding edge of Node.js enterprise frameworks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NestJS 12.0&lt;/strong&gt; brings a massive quality-of-life improvement with Native ESM, Rspack/Vitest tooling, and Standard Schema route validation, shedding years of CommonJS and &lt;code&gt;class-validator&lt;/code&gt; legacy overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Holu 3.0&lt;/strong&gt; continues to refine its radically explicit architecture, proving that you don't need magic—you just need a meticulously designed Dependency Injection hierarchy. By handling everything from parameter validation (via Factory Providers) to route interception within the boundaries of DI, Holu offers unmatched predictability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which architectural philosophy do you prefer? The familiar, schema-friendly ecosystem of NestJS, or the strict, DI-centric predictability of Holu?&lt;/p&gt;

&lt;p&gt;Let me know in the comments below! If you haven't yet, run &lt;code&gt;npm i @nestjs/core@next&lt;/code&gt; or &lt;code&gt;npm i @holu/core@next&lt;/code&gt; and try them out for yourself.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>holu</category>
    </item>
    <item>
      <title>A Deep Dive into Node.js Framework Architectures</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sun, 26 Jul 2026 12:56:36 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-a-deep-dive-into-nodejs-framework-architectures-part-1-3l82</link>
      <guid>https://dev.to/kostyatretyak/nestjs-v120-vs-ditsmod-v30-a-deep-dive-into-nodejs-framework-architectures-part-1-3l82</guid>
      <description>&lt;p&gt;If you are building enterprise-grade Node.js applications with TypeScript, chances are you are already deeply familiar with &lt;strong&gt;NestJS&lt;/strong&gt;. It has become the de-facto standard for opinionated, heavily structured, Angular-inspired backend development.&lt;/p&gt;

&lt;p&gt;But the ecosystem is always evolving, and a formidable contender has entered the arena: &lt;a href="http://holujs.github.io/en/" rel="noopener noreferrer"&gt;Holu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like NestJS, Holu relies on decorators, Dependency Injection (DI), and a modular architecture. However, under the hood, it takes a noticeably different, highly explicit approach to provider scoping, extension initialization, and request lifecycles.&lt;/p&gt;

&lt;p&gt;In this first article of the "NestJS vs. Holu" series, we will explore the foundational architectural differences between the two frameworks.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Modules and explicit architecture
&lt;/h2&gt;

&lt;p&gt;Both frameworks organize code into logical, reusable blocks called Modules.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;NestJS&lt;/strong&gt;, you use the &lt;code&gt;@Module()&lt;/code&gt; decorator to declare &lt;code&gt;imports&lt;/code&gt;, &lt;code&gt;controllers&lt;/code&gt;, &lt;code&gt;providers&lt;/code&gt;, and &lt;code&gt;exports&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Holu&lt;/strong&gt; splits this responsibility into specialized roles, making the application graph highly explicit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@rootModule&lt;/code&gt; (usually &lt;code&gt;AppModule&lt;/code&gt;) is the single entry point.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@featureModule&lt;/code&gt; encapsulates reusable providers.&lt;/li&gt;
&lt;li&gt;Specialized module decorators like &lt;code&gt;@restModule&lt;/code&gt; or &lt;code&gt;@trpcModule&lt;/code&gt; add transport-specific capabilities (like &lt;code&gt;controllers&lt;/code&gt; and route &lt;code&gt;appends&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Collision Resolution
&lt;/h3&gt;

&lt;p&gt;One of the most distinct architectural choices in Holu is how it handles token collisions. If you import two modules in NestJS that export the same provider token, the framework resolves it silently based on import order.&lt;/p&gt;

&lt;p&gt;Holu explicitly rejects this ambiguity. If a collision occurs, Holu throws an error and forces you to resolve it manually using arrays like &lt;code&gt;resolvedCollisionsPer*&lt;/code&gt;. This strictness prevents hard-to-debug DI overrides in massive codebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Dependency Injection (DI) Hierarchies
&lt;/h2&gt;

&lt;p&gt;NestJS handles provider scope using the &lt;code&gt;Scope&lt;/code&gt; enum (&lt;code&gt;DEFAULT&lt;/code&gt;, &lt;code&gt;REQUEST&lt;/code&gt;, &lt;code&gt;TRANSIENT&lt;/code&gt;). A Request-scoped provider in NestJS bubbles up, causing any service that injects it to also become Request-scoped, which can sometimes lead to performance bottlenecks.&lt;/p&gt;

&lt;p&gt;Holu approaches DI through a strict &lt;strong&gt;Injector Hierarchy&lt;/strong&gt;. There are four distinct levels of injectors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;providersPerApp&lt;/code&gt; (Application singleton)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;providersPerMod&lt;/code&gt; (Module singleton)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;providersPerRou&lt;/code&gt; (Route singleton)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;providersPerReq&lt;/code&gt; (HTTP request instance)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rule is simple but powerful: &lt;strong&gt;Child injectors look up to parents, but parents never look down.&lt;/strong&gt; If a service is registered in &lt;code&gt;providersPerApp&lt;/code&gt;, it cannot inject a service from &lt;code&gt;providersPerReq&lt;/code&gt;. Furthermore, if you override a provider at the &lt;code&gt;Req&lt;/code&gt; level, it shadows the parent provider without affecting the &lt;code&gt;App&lt;/code&gt; or &lt;code&gt;Mod&lt;/code&gt; levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Extending the Framework: Lifecycle vs. Extensions
&lt;/h2&gt;

&lt;p&gt;To hook into the framework setup, NestJS provides lifecycle interfaces: &lt;code&gt;OnModuleInit&lt;/code&gt;, &lt;code&gt;OnApplicationBootstrap&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Holu completely redesigns this concept with its &lt;strong&gt;Extensions System&lt;/strong&gt;. In Holu, extensions are isolated infrastructure hooks that run &lt;em&gt;after&lt;/em&gt; metadata collection but &lt;em&gt;before&lt;/em&gt; the request handlers are created.&lt;/p&gt;

&lt;p&gt;Instead of simple lifecycle hooks, Holu extensions implement up to three sequential stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stage1(isLastModule)&lt;/code&gt;&lt;/strong&gt;: Used to dynamically collect metadata, build routes, and push providers into arrays dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stage2(injectorPerMod)&lt;/code&gt;&lt;/strong&gt;: Runs after &lt;code&gt;stage1&lt;/code&gt; completes for all modules, receiving a fully formed module-level injector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stage3()&lt;/code&gt;&lt;/strong&gt;: Final post-processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, Holu extensions can form &lt;strong&gt;Groups&lt;/strong&gt;. Multiple extensions can contribute data to a single "Lead Extension," allowing plugins (like OpenAPI generators or custom body parsers) to seamlessly weave into the routing pipeline without messy workarounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The REST Request Lifecycle
&lt;/h2&gt;

&lt;p&gt;In NestJS, an incoming HTTP request flows through a well-known pipeline:&lt;br&gt;
&lt;code&gt;Middleware -&amp;gt; Guards -&amp;gt; Interceptors -&amp;gt; Pipes -&amp;gt; Controllers -&amp;gt; Filters&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Holu manages HTTP requests via the &lt;code&gt;@holu/rest&lt;/code&gt; package, executing a streamlined, nested interceptor chain managed by the &lt;code&gt;RequestDispatcher&lt;/code&gt;. The flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpFrontend&lt;/code&gt;&lt;/strong&gt;: Parses path and query parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GuardedInterceptor&lt;/code&gt;&lt;/strong&gt;: Executes &lt;code&gt;CanActivate&lt;/code&gt; guards. (If a guard fails, execution stops immediately).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom `HTTP_INTERCEPTORS&lt;/strong&gt;`: User-defined or module-defined interceptors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpBackend&lt;/code&gt;&lt;/strong&gt;: Invokes the actual controller method.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you need to wrap the &lt;em&gt;entire&lt;/em&gt; request (for instance, to set up an OpenTelemetry trace that includes the Guard execution), Holu allows you to override the &lt;code&gt;RequestDispatcher&lt;/code&gt; entirely at the &lt;code&gt;providersPerApp&lt;/code&gt; level.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Task Scheduling
&lt;/h2&gt;

&lt;p&gt;Both frameworks offer elegant, decorator-driven task scheduling using cron jobs, timeouts, and intervals.&lt;/p&gt;

&lt;p&gt;In NestJS, you import the &lt;code&gt;ScheduleModule&lt;/code&gt; and use &lt;code&gt;@Cron()&lt;/code&gt;, &lt;code&gt;@Interval()&lt;/code&gt;, or &lt;code&gt;@Timeout()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Holu provides &lt;code&gt;@holu/schedule&lt;/code&gt; with practically identical decorators (&lt;code&gt;@cron&lt;/code&gt;, &lt;code&gt;@interval&lt;/code&gt;, &lt;code&gt;@timeout&lt;/code&gt;) and a &lt;code&gt;SchedulerRegistry&lt;/code&gt; for runtime management. However, true to its architectural strictness, Holu enforces DI scope constraints on your scheduled tasks:&lt;br&gt;
&lt;strong&gt;Task classes MUST be registered in &lt;code&gt;providersPerMod&lt;/code&gt; or &lt;code&gt;providersPerApp&lt;/code&gt;.&lt;/strong&gt;&lt;br&gt;
If you attempt to register a cron job in &lt;code&gt;providersPerRou&lt;/code&gt; or &lt;code&gt;providersPerReq&lt;/code&gt;, Holu will actively block it and emit a warning, preventing memory leaks and orphaned intervals that often plague request-scoped scheduling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;NestJS is a fantastic, battle-tested framework. However, Holu's highly explicit DI hierarchy, strict collision handling, and powerful three-stage Extension system present a compelling alternative for developers who want absolute control over their application's infrastructure and dependency graph.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt; of this series, we will dive deeper into the code. We will compare how to write custom interceptors, handle complex parameter validation (Pipes vs. Holu Factory Providers), and benchmark the request lifecycle.&lt;/p&gt;

&lt;p&gt;Have you tried Holu yet? Let me know your thoughts on its strict DI approach in the comments!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>node</category>
      <category>holu</category>
    </item>
    <item>
      <title>Testing "quickie posts" on dev.to</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sat, 30 Nov 2024 10:32:32 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/testing-quickie-posts-on-devto-12c1</link>
      <guid>https://dev.to/kostyatretyak/testing-quickie-posts-on-devto-12c1</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/kostyatretyak" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F398023%2F5deaa82a-162c-4f3e-a075-99469a954954.jpg" alt="kostyatretyak"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/kostyatretyak/nestjs-vs-ditsmod-pipe-features-27f1" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;NestJS vs. Ditsmod: pipe features&lt;/h2&gt;
      &lt;h3&gt;Костя Третяк ・ Oct 3&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#ditsmod&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#nestjs&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#express&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#fastify&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>A quick overview of Bun's basic features and how it compares to Node.js</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Wed, 28 Aug 2024 06:12:00 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/a-quick-overview-of-buns-basic-features-and-a-comparison-with-nodejs-3o1a</link>
      <guid>https://dev.to/kostyatretyak/a-quick-overview-of-buns-basic-features-and-a-comparison-with-nodejs-3o1a</guid>
      <description>&lt;p&gt;Bun is a JavaScript runtime, positioned by its author as a set of ready-to-use tools "out of the box" provided along with the JavaScript runtime: this includes a package manager, a bundler, and a test runner. Additionally, the author strives to offer strong TypeScript support so that there’s no need to compile TypeScript files into JavaScript files. It's been almost a year since the first version of Bun was released. In this short post, I will examine how well the claimed features align with reality and the performance it delivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Bun
&lt;/h2&gt;

&lt;p&gt;As shown on &lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun's main page&lt;/a&gt;, on Linux or macOS, it can be installed as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://bun.sh/install | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can be upgraded to the next stable version with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun upgrade &lt;span class="nt"&gt;--stable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cloning the Repository with Benchmark Code
&lt;/h2&gt;

&lt;p&gt;In this article, we will use the code that can be found &lt;a href="https://github.com/KostyaTretyak/bun-vs-node" rel="noopener noreferrer"&gt;in my repository&lt;/a&gt;. Clone it, navigate to the new directory, and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/KostyaTretyak/bun-vs-node.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bun-vs-node
bun &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, install the &lt;code&gt;wrk&lt;/code&gt; benchmarking utility on your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing the Speed of Bun's Native Web Server and Node.js
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Node.js on single process
&lt;/h3&gt;

&lt;p&gt;First, let's see what Node.js shows, and then compare these results with Bun benchmarks. So, in the first terminal, run the &lt;code&gt;node.mjs&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node node.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, the cold start takes about 5ms.&lt;/p&gt;

&lt;p&gt;Now, in the second terminal, run the benchmark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wrk &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Connection: keep-alive'&lt;/span&gt; &lt;span class="nt"&gt;--latency&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; 5 &lt;span class="nt"&gt;-c&lt;/span&gt; 256 &lt;span class="nt"&gt;--timeout&lt;/span&gt; 8 &lt;span class="nt"&gt;-t&lt;/span&gt; 4 http://localhost:3000/plaintext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command makes requests in four threads, with a concurrency of 256, for a duration of 5 seconds. On my machine, the average result is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 5s test @ http://localhost:3000/plaintext
  4 threads and 256 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    73.32ms  222.54ms   1.83s    92.72%
    Req/Sec     4.50k   665.94     6.39k    76.50%
  Latency Distribution
     50%   13.74ms
     75%   14.93ms
     90%   79.47ms
     99%    1.23s 
  89513 requests in 5.02s, 16.39MB read
Requests/sec:  17846.79
Transfer/sec:      3.27MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, about 17 thousand requests per second.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node.js with cluster mode
&lt;/h3&gt;

&lt;p&gt;Now let's try using the cluster mode. To do this, go back to the first terminal, stop the process by pressing &lt;code&gt;Ctrl+C&lt;/code&gt;, and run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node node-cluster.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, it has 4 cores, and each process in the cluster starts twice as slowly - about 10ms on average.&lt;/p&gt;

&lt;p&gt;Now, run the benchmark again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wrk &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Connection: keep-alive'&lt;/span&gt; &lt;span class="nt"&gt;--latency&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; 5 &lt;span class="nt"&gt;-c&lt;/span&gt; 256 &lt;span class="nt"&gt;--timeout&lt;/span&gt; 8 &lt;span class="nt"&gt;-t&lt;/span&gt; 4 http://localhost:3000/plaintext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the results are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 5s test @ http://localhost:3000/plaintext
  4 threads and 256 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     9.03ms   18.69ms 345.62ms   97.60%
    Req/Sec     9.26k     1.55k   12.23k    83.50%
  Latency Distribution
     50%    6.42ms
     75%    8.32ms
     90%   10.85ms
     99%   85.68ms
  184883 requests in 5.03s, 33.85MB read
Requests/sec:  36720.73
Transfer/sec:      6.72MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, 36 thousand requests per second, which is slightly more than twice as fast compared to running on a single core without cluster mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bun on single process
&lt;/h3&gt;

&lt;p&gt;Now let's run the same benchmarks with Bun. Go to the first terminal, press &lt;code&gt;Ctrl+C&lt;/code&gt;, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun bun.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The process starts in about 4ms (almost as fast as in Node.js). When running the same benchmark with &lt;code&gt;wrk&lt;/code&gt;, the following result is obtained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 5s test @ http://localhost:3000/plaintext
  4 threads and 256 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.42ms  769.54us  26.37ms   93.00%
    Req/Sec    11.87k     0.86k   12.95k    75.00%
  Latency Distribution
     50%    5.28ms
     75%    5.80ms
     90%    6.04ms
     99%    6.77ms
  236197 requests in 5.04s, 31.99MB read
Requests/sec:  46849.13
Transfer/sec:      6.34MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, Bun handles 46 thousand requests per second on a single core, which is 2.5 times faster compared to Node.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bun with cluster mode
&lt;/h3&gt;

&lt;p&gt;Again, go to the first terminal, press &lt;code&gt;Ctrl+C&lt;/code&gt;, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun bun-cluster.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see that in cluster mode, Bun's processes start just as quickly as without cluster mode.&lt;/p&gt;

&lt;p&gt;Now, run the benchmark with &lt;code&gt;wrk&lt;/code&gt; again, and the following result is obtained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 5s test @ http://localhost:3000/plaintext
  4 threads and 256 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.09ms    2.08ms  32.22ms   70.02%
    Req/Sec    19.97k     1.99k   27.94k    85.50%
  Latency Distribution
     50%    2.86ms
     75%    4.16ms
     90%    5.70ms
     99%    9.41ms
  399353 requests in 5.07s, 54.08MB read
Requests/sec:  78753.89
Transfer/sec:     10.66MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, 78 thousand requests per second. There is a performance increase, but only about 1.5 times compared to non-cluster mode. However, this is twice as fast as Node.js in cluster mode.&lt;/p&gt;

&lt;p&gt;Describing a summary from my benchmarking experience, I can say that these are the best results comparing "Bun vs Node.js". It seems that Bun's web server is twice (or sometimes four times) faster, but this speed does not extend to all code. As you gradually increase the amount of JavaScript code in your application, Bun's performance will quickly degrade.&lt;/p&gt;

&lt;p&gt;And when Bun works with database code, it shows noticeably worse results compared to Node.js. As far as I understand, this happens due to the significant amount of JavaScript code in database drivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Bun Works with ExpressJS
&lt;/h2&gt;

&lt;p&gt;Run it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun express.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, Bun launches this code in about 10ms, while Node.js launches it in 4ms, meaning Node.js has a 2.5 times faster cold start.&lt;/p&gt;

&lt;p&gt;However, &lt;a href="https://bun.sh/docs/bundler/executables" rel="noopener noreferrer"&gt;Bun has a special feature designed to minimize startup time&lt;/a&gt;. To use it, create an executable file using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun build ./express.mjs &lt;span class="nt"&gt;--compile&lt;/span&gt; &lt;span class="nt"&gt;--minify&lt;/span&gt; &lt;span class="nt"&gt;--outfile&lt;/span&gt; mycli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will generate a &lt;code&gt;mycli&lt;/code&gt; file from &lt;code&gt;express.mjs&lt;/code&gt;, which can be run as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mycli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, this file launches slightly slower (about one millisecond slower) than the uncompiled file. However, this feature is primarily intended for large projects, so no optimization for cold start is noticeable in minimal code. However, the speed of the compiled file is slightly higher.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Bun Works with Ditsmod
&lt;/h2&gt;

&lt;p&gt;If you clone the seeds from &lt;a href="https://ditsmod.github.io/en/" rel="noopener noreferrer"&gt;Ditsmod&lt;/a&gt; and install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/ditsmod/seed.git my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
bun &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And try to immediately run the application using Bun:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun run build
bun dist/main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will throw the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 | (function (entry, fetcher)
    ^
SyntaxError: export 'ValueProvider' not found in './types-and-models.js'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/oven-sh/bun/issues/10438" rel="noopener noreferrer"&gt;This bug&lt;/a&gt; is easily fixed - just delete all tsconfig files from &lt;code&gt;node_modules/@ditsmod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;node_modules/@ditsmod/&lt;span class="k"&gt;*&lt;/span&gt;/tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, now Bun works as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun dist/main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Personally, I don't yet see any significant advantages for switching to Bun. It has fast dependency installation, a fast web server, and possibly fast SQLite. If you open &lt;a href="https://github.com/oven-sh/bun/issues" rel="noopener noreferrer"&gt;its repository&lt;/a&gt;, you can see that the number of bugs is overwhelming, so the Bun team has a hard time handling them.&lt;/p&gt;

&lt;p&gt;If nothing significant happens in Bun's development over the next year, it will be unpromising for me.&lt;/p&gt;

</description>
      <category>node</category>
      <category>bunjs</category>
      <category>express</category>
      <category>ditsmod</category>
    </item>
    <item>
      <title>@ts-stack/multer simplifies uploading files to a Node.js-based backend</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Mon, 29 Jul 2024 05:17:02 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/ts-stackmulter-simplifies-uploading-files-to-a-nodejs-based-backend-1fk1</link>
      <guid>https://dev.to/kostyatretyak/ts-stackmulter-simplifies-uploading-files-to-a-nodejs-based-backend-1fk1</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/ts-stack/multer" rel="noopener noreferrer"&gt;This package&lt;/a&gt; is actually a fork of the well-known native package for ExpressJS multer v2.0.0-rc.4. It will be interesting primarily to those developers who prefer Promise-style programming instead of middleware. In addition, it is also important that this package is written in TypeScript, so the type support and context documentation in it is top notch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Make sure you have Node.js &amp;gt;= v20.0.6 installed. The package can be installed with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ts-stack/multer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Multer returns an object with four properties: &lt;code&gt;textFields&lt;/code&gt;, &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;files&lt;/code&gt; and &lt;code&gt;groups&lt;/code&gt;. The &lt;code&gt;textFields&lt;/code&gt; object contains the values of the text fields of the form, the &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;files&lt;/code&gt; or &lt;code&gt;groups&lt;/code&gt; object contains the files (as &lt;code&gt;Readable&lt;/code&gt; stream) uploaded via the form.&lt;/p&gt;

&lt;p&gt;The following example uses ExpressJS only for simplicity. In fact, &lt;code&gt;@ts-stack/multer&lt;/code&gt; does not return middleware, so it is less convenient for ExpressJS than the &lt;a href="https://github.com/expressjs/multer/tree/v2.0.0-rc.4" rel="noopener noreferrer"&gt;original module&lt;/a&gt;. Basic usage example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Multer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ts-stack/multer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createWriteStream&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Here `avatar`, `photos` and `gallery` - is the names of the field in the HTML form.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Multer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fileSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10MB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseAvatar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsePhotos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;photos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseGroups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gallery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;parseAvatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.file is the `avatar` file&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.textFields will hold the text fields, if there were any&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`uploaded-files/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;parsedForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writableStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;parsedForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;writableStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/photos/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;parsePhotos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.files is array of `photos` files&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.textFields will contain the text fields, if there were any&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;parsedForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`uploaded-files/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writableStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;writableStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;writableStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;writableStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cool-profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;parseGroups&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.groups is an object (String -&amp;gt; Array) where fieldname is the key, and the value is array of files&lt;/span&gt;
  &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="c1"&gt;// e.g.&lt;/span&gt;
  &lt;span class="c1"&gt;//  parsedForm.groups['avatar'][0] -&amp;gt; File&lt;/span&gt;
  &lt;span class="c1"&gt;//  parsedForm.groups['gallery'] -&amp;gt; Array&lt;/span&gt;
  &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.textFields will contain the text fields, if there were any&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case you need to handle a text-only multipart form, you can use the &lt;code&gt;.textFields()&lt;/code&gt; method, example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Multer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ts-stack/multer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsetextFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Multer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;textFields&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;parsetextFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// parsedForm.textFields contains the text fields&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Error handling
&lt;/h2&gt;

&lt;p&gt;This is a list of error codes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorMessageCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CLIENT_ABORTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client aborted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIMIT_FILE_SIZE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File too large&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIMIT_FILE_COUNT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too many files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIMIT_FIELD_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Field name too long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIMIT_FIELD_VALUE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Field value too long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIMIT_FIELD_COUNT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too many fields&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIMIT_UNEXPECTED_FILE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unexpected file field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see these error codes in the &lt;code&gt;MulterError#code&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Multer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MulterError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ErrorMessageCode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ts-stack/multer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Multer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;MulterError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="c1"&gt;// This property is of type ErrorMessageCode.&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>node</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The "@ts-stack/body-parser" package passes the parsing results via a Promise</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Mon, 22 Jul 2024 08:13:59 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/the-ts-stackbody-parser-package-passes-the-parsing-results-via-a-promise-3fg7</link>
      <guid>https://dev.to/kostyatretyak/the-ts-stackbody-parser-package-passes-the-parsing-results-via-a-promise-3fg7</guid>
      <description>&lt;p&gt;The package &lt;a href="https://github.com/ts-stack/body-parser" rel="noopener noreferrer"&gt;@ts-stack/body-parser&lt;/a&gt; is a fork of &lt;a href="https://github.com/expressjs/body-parser" rel="noopener noreferrer"&gt;body-parser&lt;/a&gt; - the native ExpressJS package. It is written in TypeScript in ESM format, without support for older versions of Node.js &amp;lt; v20.6.0. To install it, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @ts-stack/body-parser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This package parses the body of an HTTP request that has the following types: JSON, Raw, Text, URL-encoded. You can use it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getJsonParser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ts-stack/body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getJsonParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1kb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;jsonParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;you posted:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handling an error&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can use the &lt;code&gt;BodyParserGroup&lt;/code&gt; helper. It is designed for cases when you do not know which parser is needed for a specific route. When creating an instance of the &lt;code&gt;BodyParserGroup&lt;/code&gt; class, you can pass options for the corresponding parsers, after which you can use the &lt;code&gt;parse&lt;/code&gt; method as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BodyParserGroup&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ts-stack/body-parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyParserGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BodyParserGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;jsonOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jsonOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;textOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;urlencodedOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlencodedOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;rawOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bodyParserGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>node</category>
    </item>
    <item>
      <title>Passing metadata from TypeScript code to JavaScript code using decorators for Dependency Injection</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Thu, 04 Jul 2024 12:51:40 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/passing-metadata-from-typescript-code-to-javascript-code-using-decorators-for-dependency-injection-162i</link>
      <guid>https://dev.to/kostyatretyak/passing-metadata-from-typescript-code-to-javascript-code-using-decorators-for-dependency-injection-162i</guid>
      <description>&lt;p&gt;When TypeScript decorators are used for Dependency Injection, they are needed for two things. First, they signal to the TypeScript compiler that it needs to read TypeScript types, transform them into JavaScript code, and save them. Second, if additional metadata is passed to the decorator factory, it must also be stored. This may surprise many, but in the first case, decorators are not used for their intended purpose in relation to how they should be used in JavaScript code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring TypeScript projects for Dependency Injection
&lt;/h2&gt;

&lt;p&gt;If you want to take advantage of TypeScript for Dependency Injection, you need to write the following in your tsconfig files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"experimentalDecorators"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"emitDecoratorMetadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, you need to add the &lt;code&gt;reflect-metadata&lt;/code&gt; library as a project dependency, and also import it in the entry file (usually this file is called &lt;code&gt;main.ts&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflect-metadata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Your code here...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that such an import must be unique for your entire project. In addition, for testing it is also necessary to make such an import if there is no &lt;code&gt;main.ts&lt;/code&gt; import in a particular test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unusual use of decorators in TypeScript projects
&lt;/h2&gt;

&lt;p&gt;You may be wondering why the use of decorators might be unusual. Let's look at the simplest example first without a decorator and then with a fake decorator. So, let's say you have two classes, and the second class depends on the first class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflect-metadata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service1&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Service1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the TypeScript compiler on this file, it will output the following JavaScript code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, without decorators, the TypeScript compiler does not transfer information about what type the &lt;code&gt;service1&lt;/code&gt; property in &lt;code&gt;Service2&lt;/code&gt; should be to the JavaScript code. In this case, this information is actually lost during compilation.&lt;/p&gt;

&lt;p&gt;Now let's add any decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reflect-metadata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service1&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nx"&gt;fakeDecorator&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Service1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fakeDecorator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the TypeScript compiler will already output the following JavaScript code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;__decorate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decorate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;__metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;Service2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;service1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;Service2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;fakeDecorator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:paramtypes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Service1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;Service2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fakeDecorator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the very presence of any decorator at the class level signals the TypeScript compiler that it needs to transfer information about the constructor parameters of this class to the JavaScript code. This information is stored using the &lt;code&gt;reflect-metadata&lt;/code&gt; library, more precisely - using the &lt;a href="https://github.com/rbuckton/reflect-metadata?tab=readme-ov-file#api" rel="noopener noreferrer"&gt;Reflect&lt;/a&gt; class from this library.&lt;/p&gt;

&lt;p&gt;As you can guess, now it remains to read the stored information using the same class &lt;a href="https://github.com/rbuckton/reflect-metadata?tab=readme-ov-file#api" rel="noopener noreferrer"&gt;Reflect&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Therefore, if there is any decorator at the class level, the TypeScript compiler passes metadata from the TypeScript code to the JavaScript code using its own decorator, and the user-defined decorator is called dynamically within its own decorator body. Thus, you can use a function with an empty body as a decorator, but the TypeScript compiler will still transfer the metadata into the JavaScript code.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>angular</category>
      <category>nestjs</category>
      <category>ditsmod</category>
    </item>
    <item>
      <title>Who knows the general current statistics on dev.to?</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Sun, 16 Jul 2023 11:07:41 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/who-knows-the-general-current-statistics-on-devto-oab</link>
      <guid>https://dev.to/kostyatretyak/who-knows-the-general-current-statistics-on-devto-oab</guid>
      <description>&lt;p&gt;Although I have published 10 posts on dev.to since 2021, the statistics of views of these posts are very demotivating, because they get only a few dozen views for the whole time, up to a maximum of 200. For my posts, I use such popular tags as &lt;code&gt;#node&lt;/code&gt;, &lt;code&gt;#nestjs&lt;/code&gt;, &lt;code&gt;#typescript&lt;/code&gt; (and the unpopular &lt;code&gt;#ditsmod&lt;/code&gt; tag).&lt;/p&gt;

&lt;p&gt;In general, few articles marked with these tags are published on dev.to per day. If you believe the statistics provided by &lt;a href="https://www.similarweb.com/website/dev.to/#overview"&gt;similarweb&lt;/a&gt;, dev.to has 12.4 million visits per month. Given that users are most active on weekdays, we can assume that dev.to has about 12400/22 ~ 560K visits per day.&lt;/p&gt;

&lt;p&gt;Thus, all posts on dev.to have approximately 560K visits per day. Unfortunately, I don't know the number of followers to the tags I've specified. But a tag like &lt;code&gt;#node&lt;/code&gt; should theoretically have a lot of followers.&lt;/p&gt;

&lt;p&gt;So, why is the number of views of posts marked with popular tags so low?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>meta</category>
    </item>
    <item>
      <title>DI providers collision in Ditsmod applications</title>
      <dc:creator>Костя Третяк</dc:creator>
      <pubDate>Tue, 21 Feb 2023 11:35:29 +0000</pubDate>
      <link>https://dev.to/kostyatretyak/di-providers-collision-in-ditsmod-applications-i3e</link>
      <guid>https://dev.to/kostyatretyak/di-providers-collision-in-ditsmod-applications-i3e</guid>
      <description>&lt;p&gt;During the process of importing &lt;a href="https://ditsmod.github.io/en/" rel="noopener noreferrer"&gt;Ditsmod&lt;/a&gt; modules, there may be a situation where among these modules there can be two (or more) modules that export non-identical providers with the same token. For example, a provider with the token Service1 can be exported from two modules simultaneously to the current module, in which case the developer may not know which version of the provider will be used. To avoid such ambiguity, &lt;a href="https://ditsmod.github.io/en/" rel="noopener noreferrer"&gt;Ditsmod&lt;/a&gt; throws an error.&lt;/p&gt;

&lt;p&gt;This feature is especially useful when importing third-party modules and you don’t have the ability to control the import order. Now let’s take a closer look at when a provider collision can occur.&lt;/p&gt;

&lt;p&gt;Imagine you have &lt;code&gt;Module3&lt;/code&gt; where you imported &lt;code&gt;Module2&lt;/code&gt; and &lt;code&gt;Module1&lt;/code&gt;. You did this import because you need &lt;code&gt;Service2&lt;/code&gt; and &lt;code&gt;Service1&lt;/code&gt; from these modules, respectively. You are viewing how these services work, but for some reason &lt;code&gt;Service1&lt;/code&gt; does not work as expected. You start debug and it turns out that &lt;code&gt;Service1&lt;/code&gt; exports both modules: &lt;code&gt;Module2&lt;/code&gt; and &lt;code&gt;Module1&lt;/code&gt;. You expected that &lt;code&gt;Service1&lt;/code&gt; would only be exported from &lt;code&gt;Module1&lt;/code&gt;, but the version exported from &lt;code&gt;Module2&lt;/code&gt; actually worked.&lt;/p&gt;

&lt;p&gt;To prevent this from happening, if you import two or more modules that export non-identical providers with the same token, Ditsmod will throw the following error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Error: Importing providers to Module3 failed: exports from Module2 and Module1 causes collision with Service1. If Module3 declared in your application (it is not imported from node_modules), you should add Service1 to resolvedCollisionsPer* in this module. For example: resolvedCollisionsPerReq: [ [Service1, Module1] ].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Specifically in this case:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Module2&lt;/code&gt; substitute and then exports the provider with the token &lt;code&gt;Service1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;and &lt;code&gt;Module1&lt;/code&gt; substitute and then exports the provider with the token &lt;code&gt;Service1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;providers with token &lt;code&gt;Service1&lt;/code&gt; are not identical in &lt;code&gt;Module2&lt;/code&gt; and &lt;code&gt;Module1&lt;/code&gt;, ie from module &lt;code&gt;Module2&lt;/code&gt; can be exported, for example, object &lt;code&gt;{ token: Service1, useValue: {} }&lt;/code&gt;, and from &lt;code&gt;Module1&lt;/code&gt;&lt;code&gt;Service1&lt;/code&gt; can be exported as a class.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And since both of these modules are imported into &lt;code&gt;Module3&lt;/code&gt;, this causes a "provider collisions", because the developer may not know which of these substitutions will work in &lt;code&gt;Module3&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collision resolution
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;Module3&lt;/code&gt; is declared in your application (it is not imported from &lt;code&gt;node_modules&lt;/code&gt;), the collision is resolved by adding to &lt;code&gt;resolvedCollisionsPer*&lt;/code&gt; an array of two elements, with the provider's token in the first place and the module from which the provider needs to be taken in the second place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Service1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./module1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./module2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;featureModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Module2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Module1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;resolvedCollisionsPerReq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Service1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Module1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Module3&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have installed &lt;code&gt;Module3&lt;/code&gt; using packages manager (npm, yarn, etc.), there is no point in modifying this module locally to resolve the collision.&lt;/p&gt;

&lt;p&gt;This situation can only occur if &lt;code&gt;Module2&lt;/code&gt; and &lt;code&gt;Module1&lt;/code&gt; are exported from the root module, so you need to remove one of these modules from there. And, of course, after that you will have to explicitly import another module into those modules where it is needed.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
