<?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: Subhash Adhikari</title>
    <description>The latest articles on DEV Community by Subhash Adhikari (@seenu-subhash).</description>
    <link>https://dev.to/seenu-subhash</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3408071%2F1f1a03a0-c6fa-426f-92df-eb8f90d2326d.png</url>
      <title>DEV Community: Subhash Adhikari</title>
      <link>https://dev.to/seenu-subhash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/seenu-subhash"/>
    <language>en</language>
    <item>
      <title>How We Designed a Production-Grade Communication Layer for an Investment Platform</title>
      <dc:creator>Subhash Adhikari</dc:creator>
      <pubDate>Fri, 20 Feb 2026 17:46:46 +0000</pubDate>
      <link>https://dev.to/seenu-subhash/how-we-designed-a-production-grade-communication-layer-for-an-investment-platform-2a95</link>
      <guid>https://dev.to/seenu-subhash/how-we-designed-a-production-grade-communication-layer-for-an-investment-platform-2a95</guid>
      <description>&lt;p&gt;Most teams building "groups and channels" underestimate how complex it gets when you add paid subscriptions, archive cutoffs, re-subscribe backfill logic, secure media, realtime delivery, and audit/compliance needs.&lt;/p&gt;

&lt;p&gt;We recently designed a standalone &lt;strong&gt;Communication Layer&lt;/strong&gt; for an investment platform that needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📢 Admin-only broadcast channels&lt;/li&gt;
&lt;li&gt;💬 Structured discussion forums&lt;/li&gt;
&lt;li&gt;💰 Subscription-aware access control&lt;/li&gt;
&lt;li&gt;🔒 Secure media delivery&lt;/li&gt;
&lt;li&gt;⚡ Horizontal scalability&lt;/li&gt;
&lt;li&gt;🧱 Full decoupling from payments/courses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how we approached it.&lt;/p&gt;




&lt;h2&gt;
  
  
  📢 Broadcast Channels — Store Once, Read Many
&lt;/h2&gt;

&lt;p&gt;We avoided fanout-on-write (writing per subscriber). Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each post is stored &lt;strong&gt;once per channel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Each channel has a &lt;strong&gt;monotonic sequence number&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Clients fetch using &lt;code&gt;after_seq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Realtime pushes &lt;strong&gt;IDs only&lt;/strong&gt; — &lt;code&gt;(channel_id, seq, post_id)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Full content is fetched via REST (with access checks)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;p&gt;Fanout-on-write breaks at scale. With store-once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1M users → still &lt;strong&gt;1 DB row per post&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Catch-up is deterministic&lt;/li&gt;
&lt;li&gt;No duplication bugs&lt;/li&gt;
&lt;li&gt;Easier pagination&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example feed fetch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/mobile/channels/{id}/posts?after_seq=120&amp;amp;limit=20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;channel_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;after_seq&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. Predictable. Scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Forums — Clean Separation from Broadcast
&lt;/h2&gt;

&lt;p&gt;We intentionally separated broadcast from discussion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Forum
  └── Topic
        └── Message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;forums&lt;/code&gt; → discussion room&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;topics&lt;/code&gt; → thread&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;messages&lt;/code&gt; → replies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Announcement feeds turning into chat spam&lt;/li&gt;
&lt;li&gt;Mixing system-critical updates with noise&lt;/li&gt;
&lt;li&gt;Moderation chaos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think: &lt;strong&gt;Telegram Channel + Linked Group&lt;/strong&gt; or &lt;strong&gt;Reddit Subreddit → Post → Comment&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💰 Subscription-Aware Access (Without Coupling Payments)
&lt;/h2&gt;

&lt;p&gt;The most important design decision:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Communication Layer knows nothing about subscriptions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, it calls an &lt;strong&gt;Access Provider&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;canReadChannel(userId, channelId) -&amp;gt; AccessDecision

AccessDecision:
  mode: FULL | ARCHIVE_UNTIL | DENY
  read_windows?: [{start?, end?}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this is powerful
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Courses can map to channels&lt;/li&gt;
&lt;li&gt;Subscription passes can expire&lt;/li&gt;
&lt;li&gt;Premium users can retain archive-only access&lt;/li&gt;
&lt;li&gt;Re-subscribe can restore with partial backfill&lt;/li&gt;
&lt;li&gt;All without polluting channel logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Communication Layer enforces only: &lt;code&gt;FULL&lt;/code&gt;, &lt;code&gt;ARCHIVE_UNTIL&lt;/code&gt;, or &lt;code&gt;DENY&lt;/code&gt; — and &lt;strong&gt;fails closed&lt;/strong&gt; if the provider is unavailable.&lt;/p&gt;




&lt;h2&gt;
  
  
  🗂 Handling Archive Cutoffs (Harder Than It Looks)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User subscribes &lt;strong&gt;Jan 1&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Expires &lt;strong&gt;Jan 31&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Re-subscribes &lt;strong&gt;Feb 20&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Admin policy: &lt;strong&gt;7-day backfill&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Readable windows become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(-∞, Jan 31]
[Feb 13, now]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That middle &lt;strong&gt;13-day gap&lt;/strong&gt;? Hidden.&lt;/p&gt;

&lt;p&gt;This required time-window enforcement at the query layer — applied consistently to REST, Realtime, and Signed Media URL issuance. &lt;strong&gt;Security invariants must be consistent everywhere.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Media Security — Best-Effort No-Download
&lt;/h2&gt;

&lt;p&gt;For premium investment content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All files stored &lt;strong&gt;privately&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Signed URLs with &lt;strong&gt;TTL ≤ 5 minutes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Scope-bound to &lt;code&gt;user + channel + post&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Issued &lt;strong&gt;only after access check&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;signed_url(user_id, channel_id, post_id, exp)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't pretend screenshots can be stopped — but we prevent casual link sharing.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Concurrency — Why &lt;code&gt;MAX(seq)+1&lt;/code&gt; Is Forbidden
&lt;/h2&gt;

&lt;p&gt;Never allocate sequence numbers in application code.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Wrong:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;channel_id&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="c1"&gt;-- then +1 in code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Correct — atomic counter table:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;channel_seq_counter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_seq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;next_seq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;channel_seq_counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_seq&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&gt;next_seq&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sequence allocation and post insert happen in the &lt;strong&gt;same transaction&lt;/strong&gt;. No duplicates. No race conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔁 Idempotent Publishing — 24h Replay Protection
&lt;/h2&gt;

&lt;p&gt;Admins double-click publish. Networks retry. We implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Idempotency-Key&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;Unique scope: &lt;code&gt;actor + endpoint + channel&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;24h TTL&lt;/li&gt;
&lt;li&gt;Same key + same payload → &lt;strong&gt;replay original response&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Same key + different payload → &lt;strong&gt;409 Conflict&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Atomic rule:&lt;/strong&gt; &lt;code&gt;post row + outbox row + idempotency row&lt;/code&gt; must commit in the &lt;strong&gt;same transaction&lt;/strong&gt;. No partial success allowed.&lt;/p&gt;




&lt;h2&gt;
  
  
  📡 Realtime — IDs Only, Never Content
&lt;/h2&gt;

&lt;p&gt;WebSocket model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT handshake&lt;/li&gt;
&lt;li&gt;Authorization on every subscription&lt;/li&gt;
&lt;li&gt;On publish → send delta:
&lt;/li&gt;
&lt;/ul&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;"channel_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"seq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;145&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9001&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;Client must call REST for full post content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Access can change between delta and fetch. REST re-checks entitlement. This prevents data leaks.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 Reliability Layer
&lt;/h2&gt;

&lt;p&gt;We added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transactional outbox pattern&lt;/li&gt;
&lt;li&gt;At-least-once event publishing&lt;/li&gt;
&lt;li&gt;Deduplication via &lt;code&gt;event_dedupe_key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tombstone deletes (cursor-safe)&lt;/li&gt;
&lt;li&gt;RPO 15m / RTO 2h&lt;/li&gt;
&lt;li&gt;Baseline SLO: &lt;strong&gt;99.9% API availability&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deleted posts remain in sequence:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"seq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_deleted"&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;"body_text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;This preserves pagination integrity.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Product Decisions That Shaped Architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Broadcast must stay clean → &lt;strong&gt;no replies allowed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Premium users may retain &lt;strong&gt;archive-only access&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Re-subscribe should &lt;strong&gt;not&lt;/strong&gt; expose full historical content&lt;/li&gt;
&lt;li&gt;Access must &lt;strong&gt;fail closed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Communication must remain &lt;strong&gt;independent&lt;/strong&gt; from monetization logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clean boundaries reduce long-term complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Core Tables (Simplified)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Table&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;channels&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Broadcast channel definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;posts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Channel messages (store-once)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;post_attachments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secure media references&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;forums&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discussion rooms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;topics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forum threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;messages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread replies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;visibility_rules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access window definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;idempotency_keys&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replay protection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outbox_events&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reliable event publishing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*_seq_counter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Atomic sequence allocation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything supports deterministic ordering, access-window enforcement, and production safety.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏁 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building a Telegram-style system is easy.&lt;/p&gt;

&lt;p&gt;Building one that supports paid archive cutoffs, re-subscribe backfill logic, secure premium media, concurrency-safe publish, audit logging, and horizontal scalability — is &lt;strong&gt;not trivial&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key lessons:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Separate broadcast from discussion&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate communication from monetization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce access at every boundary&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make ordering deterministic&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat reliability as first-class&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If you're building paid communities, investment signal platforms, course-based broadcast systems, or subscription media platforms — I'd love to exchange notes. Drop a comment below 👇&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>backend</category>
      <category>database</category>
    </item>
    <item>
      <title>class-validator Cheatsheet: Useful Decorators and NestJS Validation Patterns (2025)</title>
      <dc:creator>Subhash Adhikari</dc:creator>
      <pubDate>Mon, 04 Aug 2025 21:03:29 +0000</pubDate>
      <link>https://dev.to/seenu-subhash/class-validator-cheatsheet-useful-decorators-and-nestjs-validation-patterns-2025-1c43</link>
      <guid>https://dev.to/seenu-subhash/class-validator-cheatsheet-useful-decorators-and-nestjs-validation-patterns-2025-1c43</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is your complete reference for all major &lt;code&gt;class-validator&lt;/code&gt; decorators, their usage, value types, and integration patterns. Bookmark this if you’re working with DTOs in NestJS or doing input validation in any TypeScript project.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚀 Install First
&lt;/h2&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;class-validator class-transformer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ✅ Basic Usage Pattern
&lt;/h2&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;IsEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validate&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;class-validator&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;LoginDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEmail&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;password&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&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;LoginDto&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&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;errors&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;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧾 Core Decorators Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Accepted Type(s)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsString()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be a string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsBoolean()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be boolean&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsInt()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be an integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;number&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsNumber()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any number (int or float)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;number&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsEmail()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid email address&lt;/td&gt;
&lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsDate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be a Date instance&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Date&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsOptional()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip validation if undefined/null&lt;/td&gt;
&lt;td&gt;&lt;code&gt;any&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsNotEmpty()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not empty (works with string, array)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;string&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsDefined()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value must be present (not undefined)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;any&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔢 Number Validations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Min(value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum number allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Max(value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum number allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsPositive()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsNegative()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be negative&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔠 String Validations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@MinLength(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum string length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@MaxLength(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum string length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Matches(regex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must match regex pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsAlpha()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only letters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsAlphanumeric()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Letters and numbers only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsPhoneNumber()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be a valid phone number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsUrl()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be a valid URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔁 Array Validations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsArray()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be an array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ArrayMinSize(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum length of array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ArrayMaxSize(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum length of array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ArrayNotEmpty()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must not be empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsInt({ each: true })&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates each element (works with many)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔘 Enum &amp;amp; Object Validations
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ADMIN&lt;/span&gt; &lt;span class="o"&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEnum&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="nx"&gt;role&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidateNested&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Type&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;Profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@ValidateNested()&lt;/code&gt; is required for nested objects&lt;/li&gt;
&lt;li&gt;Must be combined with &lt;code&gt;@Type(() =&amp;gt; ClassName)&lt;/code&gt; from &lt;code&gt;class-transformer&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚖️ Conditional Validation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidateIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAdult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;licenseNumber&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;@ValidateIf()&lt;/code&gt; to apply a rule conditionally based on other values.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 Custom Validators
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidatorConstraint&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;IsStrongPassword&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;IsStrongPassword&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ValidatorConstraintInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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="nx"&gt;value&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;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;defaultMessage&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Password must be at least 8 chars and include an uppercase letter.&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;Use it like this:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IsStrongPassword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;password&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚙️ NestJS Global Integration
&lt;/h2&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useGlobalPipes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidationPipe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;whitelist&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="na"&gt;forbidNonWhitelisted&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="na"&gt;transform&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="na"&gt;transformOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enableImplicitConversion&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;h2&gt;
  
  
  🧠 Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Forgetting &lt;code&gt;@Type()&lt;/code&gt;&lt;/strong&gt;: Nested validation won’t work without it&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Validating plain objects&lt;/strong&gt;: Use &lt;code&gt;plainToInstance()&lt;/code&gt; from &lt;code&gt;class-transformer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Wrong order of decorators&lt;/strong&gt;: Execution is bottom-to-top, so arrange carefully&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Pro Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Combine &lt;code&gt;@IsOptional()&lt;/code&gt; with other validators to skip when empty&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;{ each: true }&lt;/code&gt; to apply decorators to every array element&lt;/li&gt;
&lt;li&gt;Custom validators = reusable logic for business rules&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;stopAtFirstError&lt;/code&gt; in &lt;code&gt;ValidationPipe&lt;/code&gt; to fail fast&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📘 &lt;a href="https://github.com/typestack/class-validator" rel="noopener noreferrer"&gt;class-validator GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📙 &lt;a href="https://github.com/typestack/class-transformer" rel="noopener noreferrer"&gt;class-transformer Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;a href="https://dev.to/subhash_adhikari_d2de106b/mastering-validation-in-typescript-with-class-validator-a-complete-beginners-guide-51lj"&gt;Full Guide Blog (Long-Form)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://www.linkedin.com/in/your-username" rel="noopener noreferrer"&gt;Connect on LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;✅ Use this cheatsheet in every TypeScript API project using DTOs. Fast, reliable, and easy to maintain.&lt;/p&gt;

</description>
      <category>classvalidator</category>
      <category>typescript</category>
      <category>decorators</category>
      <category>cheatsheet</category>
    </item>
    <item>
      <title>Mastering Validation in TypeScript with class-validator – A Complete Beginner's Guide</title>
      <dc:creator>Subhash Adhikari</dc:creator>
      <pubDate>Sat, 02 Aug 2025 15:50:43 +0000</pubDate>
      <link>https://dev.to/seenu-subhash/mastering-validation-in-typescript-with-class-validator-a-complete-beginners-guide-51lj</link>
      <guid>https://dev.to/seenu-subhash/mastering-validation-in-typescript-with-class-validator-a-complete-beginners-guide-51lj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Learn how to validate user input in TypeScript and NestJS using &lt;code&gt;class-validator&lt;/code&gt;. This in-depth tutorial covers everything from basic usage to advanced decorators, real-world use cases, and integration with NestJS — all optimized for clean code, security, and SEO.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📌 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is &lt;code&gt;class-validator&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Why Input Validation Matters&lt;/li&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Basic Example: Email and Password Validation&lt;/li&gt;
&lt;li&gt;Popular Validation Decorators&lt;/li&gt;
&lt;li&gt;Advanced Validation: Arrays, Enums, Dates&lt;/li&gt;
&lt;li&gt;Nested Object Validation&lt;/li&gt;
&lt;li&gt;Conditional Validation with &lt;code&gt;@ValidateIf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Custom Validators&lt;/li&gt;
&lt;li&gt;Full Integration with NestJS&lt;/li&gt;
&lt;li&gt;Common Pitfalls and Mistakes&lt;/li&gt;
&lt;li&gt;Final Takeaways&lt;/li&gt;
&lt;li&gt;Connect with Me&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🧠 What is &lt;code&gt;class-validator&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/typestack/class-validator" rel="noopener noreferrer"&gt;&lt;code&gt;class-validator&lt;/code&gt;&lt;/a&gt; is a powerful library that allows you to use decorators to validate TypeScript class properties. It's most commonly used with frameworks like NestJS or in pure Node.js apps to enforce runtime validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  ❗ Why Input Validation Matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔐 Prevents invalid or malicious data&lt;/li&gt;
&lt;li&gt;🧼 Ensures clean, predictable input&lt;/li&gt;
&lt;li&gt;📉 Reduces bugs in business logic&lt;/li&gt;
&lt;li&gt;🔧 Improves API documentation and usability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚙️ Installation
&lt;/h2&gt;

&lt;p&gt;Install it along with &lt;code&gt;class-transformer&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;npm &lt;span class="nb"&gt;install &lt;/span&gt;class-validator class-transformer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧪 Basic Example: Email and Password Validation
&lt;/h2&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;IsEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MinLength&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;class-validator&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;CreateUserDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEmail&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;password&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;validate&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;class-validator&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;user&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;CreateUserDto&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;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wrong-format&lt;/span&gt;&lt;span class="dl"&gt;'&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;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&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;errors&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;validate&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;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="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; array of error messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📋 Popular Validation Decorators
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsString()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensure field is a string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsEmail()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate email format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsInt()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensure field is an integer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@Min()&lt;/code&gt; / &lt;code&gt;@Max()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Number boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@MinLength()&lt;/code&gt; / &lt;code&gt;@MaxLength()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;String length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsBoolean()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must be true/false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@IsOptional()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skips validation if not present&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💡 Advanced Validation: Arrays, Enums, Dates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Validate Arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsArray&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsInt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;each&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="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate Enums
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ADMIN&lt;/span&gt; &lt;span class="o"&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEnum&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="nx"&gt;role&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate Dates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsDate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;MinDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nx"&gt;joinDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧱 Nested Object Validation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Profile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;bio&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidateNested&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Type&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;Profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&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;Always include &lt;code&gt;@Type()&lt;/code&gt; from &lt;code&gt;class-transformer&lt;/code&gt;, or validation will silently fail.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚖️ Conditional Validation with &lt;code&gt;@ValidateIf&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidateIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;licenseNumber&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This only validates &lt;code&gt;licenseNumber&lt;/code&gt; if &lt;code&gt;age &amp;gt; 18&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔧 Custom Validators
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidatorConstraint&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;IsStrongPassword&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;IsStrongPassword&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;ValidatorConstraintInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&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;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;defaultMessage&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Password must have at least 8 characters and one uppercase letter.&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;Use it like this:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IsStrongPassword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;password&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Full Integration with NestJS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;main.ts&lt;/code&gt; Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useGlobalPipes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidationPipe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;whitelist&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="na"&gt;forbidNonWhitelisted&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="na"&gt;transform&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="na"&gt;transformOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;enableImplicitConversion&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;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample DTO and Controller
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// create-user.dto.ts&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;CreateUserDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEmail&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;MinLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;password&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsOptional&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&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="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// users.controller.ts&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;dto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserDto&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userService&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="nx"&gt;dto&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;NestJS will automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate the body&lt;/li&gt;
&lt;li&gt;Convert string to numbers if needed&lt;/li&gt;
&lt;li&gt;Throw &lt;code&gt;400 Bad Request&lt;/code&gt; on validation failure&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ❌ Common Pitfalls and Mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Forgetting &lt;code&gt;@Type()&lt;/code&gt; in Nested DTOs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidateNested&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Will not validate&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Fix:&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ValidateNested&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Type&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;Profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Wrong Order of Decorators
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsNotEmpty&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Runs bottom-up: IsNotEmpty first, then IsEmail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always order carefully if decorators depend on each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Not Using &lt;code&gt;plainToInstance()&lt;/code&gt;
&lt;/h3&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;plainToInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CreateUserDto&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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;validate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, validation may silently fail if using plain JS objects.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Final Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;@IsOptional()&lt;/code&gt; when fields aren't required&lt;/li&gt;
&lt;li&gt;Always use &lt;code&gt;@Type()&lt;/code&gt; in nested objects&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ValidationPipe&lt;/code&gt; in NestJS for automatic validation&lt;/li&gt;
&lt;li&gt;Validate arrays with &lt;code&gt;{ each: true }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use custom validators for complex business rules&lt;/li&gt;
&lt;/ul&gt;







&lt;h3&gt;
  
  
  🔄 What About Zod?
&lt;/h3&gt;

&lt;p&gt;While this guide focuses on &lt;code&gt;class-validator&lt;/code&gt;, many developers also consider &lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;&lt;code&gt;zod&lt;/code&gt;&lt;/a&gt; for validating data in TypeScript. If you're wondering how it compares, here’s a quick breakdown to help you choose the best tool for your needs:&lt;/p&gt;

&lt;h2&gt;
  
  
  🥊 class-validator vs Zod: Which Should You Use?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Use Case&lt;/th&gt;
&lt;th&gt;✅ &lt;code&gt;class-validator&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;🔥 &lt;code&gt;zod&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TypeScript Decorator Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes (&lt;code&gt;@IsEmail()&lt;/code&gt;, &lt;code&gt;@MinLength()&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;❌ No decorators — schema-based only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OOP / Class-based structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Excellent fit (DTOs, NestJS)&lt;/td&gt;
&lt;td&gt;❌ Functional only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Functional/Composable validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;😐 Limited&lt;/td&gt;
&lt;td&gt;✅ Designed for functional composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema inference from types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Manual type declarations&lt;/td&gt;
&lt;td&gt;✅ &lt;code&gt;z.infer&amp;lt;typeof Schema&amp;gt;&lt;/code&gt; supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime type validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration with NestJS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Native via &lt;code&gt;ValidationPipe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;❌ Requires custom adapters or wrappers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Slightly heavier (uses decorators + reflection)&lt;/td&gt;
&lt;td&gt;⚡ Very fast and lightweight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom validators&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Class-based rules&lt;/td&gt;
&lt;td&gt;✅ Function-based rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Serialization support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ via &lt;code&gt;class-transformer&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;❌ Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;😐 Slightly steeper (due to decorators)&lt;/td&gt;
&lt;td&gt;✅ Beginner-friendly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  ✅ When to Use &lt;code&gt;class-validator&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're using &lt;strong&gt;NestJS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You prefer &lt;strong&gt;OOP and decorators&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;&lt;code&gt;class-transformer&lt;/code&gt;&lt;/strong&gt; for serialization&lt;/li&gt;
&lt;li&gt;Your architecture uses DTO classes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ⚡ When to Use &lt;code&gt;Zod&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You're building with &lt;strong&gt;functional programming style&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You're using &lt;strong&gt;tRPC&lt;/strong&gt;, &lt;strong&gt;Express&lt;/strong&gt;, or &lt;strong&gt;Fastify&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;fast runtime validation and type inference&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You prefer &lt;strong&gt;schema-based design&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧠 Final Word
&lt;/h3&gt;

&lt;p&gt;Both libraries are excellent. Your choice depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your framework (NestJS vs Express/tRPC)&lt;/li&gt;
&lt;li&gt;Your programming style (OOP vs functional)&lt;/li&gt;
&lt;li&gt;Your team’s preference for decorators vs schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer &lt;strong&gt;decorators and structured classes&lt;/strong&gt;, go with &lt;code&gt;class-validator&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
If you prefer &lt;strong&gt;schema inference and functional composition&lt;/strong&gt;, go with &lt;code&gt;zod&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌 Connect with Me
&lt;/h2&gt;

&lt;p&gt;If this helped you, please:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;📚 Official Repo: &lt;a href="https://github.com/typestack/class-validator" rel="noopener noreferrer"&gt;github.com/typestack/class-validator&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📬 Share your thoughts in comments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔗 &lt;a href="https://www.linkedin.com/in/subhash-adhikari-045018305/" rel="noopener noreferrer"&gt;Connect with me on LinkedIn&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Want a quick reference? I’m posting a &lt;code&gt;class-validator&lt;/code&gt; cheatsheet next! Follow me to stay updated.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>typescript</category>
      <category>nestjs</category>
      <category>validation</category>
    </item>
  </channel>
</rss>
