≡ Menu

In the world of NestJS, Data Transfer Objects (DTOs) are often treated as a boilerplate necessity—a simple class we create to satisfy the compiler.

However, if you are building complex systems like multi-tenant e-commerce platforms or AI-integrated backends, your DTOs are actually the first line of defense for your application’s integrity.

Why DTOs Matter More Than Ever

A DTO defines how data is sent over the network. By using classes coupled with decorators, NestJS allows for runtime validation, ensuring that the “junk data” never even hits your service layer.

The Standard Approach: Class-Validator

Most developers start with class-validator. It’s intuitive and integrates natively with the NestJS ValidationPipe.

import { IsString, IsEmail, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;

  @MinLength(8)
  password: string;
}

While effective, this approach can sometimes lead to “Decorator Hell,” where your DTO classes become bloated with metadata that is hard to reuse outside of the NestJS context.


The “Zod-First” Revolution

As we move toward more unified architectures (like monorepos), many developers are shifting toward Zod for DTO validation. The “Zod-first” approach allows you to define a single schema that can be shared between your backend validation and your frontend forms (e.g., in a Next.js storefront).

Implementing Zod DTOs in NestJS

To use Zod, you can leverage the nestjs-zod library or create a custom pipe. This allows for incredibly powerful, functional validation logic that feels more like TypeScript and less like annotation.

import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';

const CreateStoreSchema = z.object({
  name: z.string().min(3).max(50),
  subdomain: z.string().toLowerCase().regex(/^[a-z0-9-]+$/),
  plan: z.enum(['LAUNCH', 'SCALE', 'ENTERPRISE']),
});

export class CreateStoreDto extends createZodDto(CreateStoreSchema) {}

Key Benefits:

  • Deep Type Safety: Infers TypeScript types directly from the schema.

  • Transformation: Automatically handles data casting (e.g., converting a string to a Date object).

  • Shared Logic: Use the exact same CreateStoreSchema in your frontend dashboard to show real-time validation errors.


DTO Strategies for Multi-Tenant Systems

When building platforms that handle sensitive data across different “tenants” or “merchants,” your DTOs should be context-aware.

1. Partial Types for Updates

Don’t rewrite your DTOs for update operations. Use the PartialType utility to keep your code DRY (Don’t Repeat Yourself).

export class UpdateStoreDto extends PartialType(CreateStoreDto) {}

2. Stripping Sensitive Fields

Always ensure your ValidationPipe is configured to whitelist: true. This prevents “over-posting,” where a malicious user tries to inject a role: 'admin' field into a DTO that shouldn’t have it.

3. Handling AI Metadata

If your application integrates AI agents, your DTOs may need to handle structured “tool-calling” data. Using Zod’s passthrough() or strict() modes helps you control exactly how much “extra” data an AI agent is allowed to send to your API.


Best Practices for 2026

  • Always use Classes: Even if using Zod, wrap schemas in classes so the NestJS IoC container can recognize them for Dependency Injection and Swagger documentation.

  • Map, Don’t Leak: Use DTOs to map your Database Entities to API Responses. Never return your raw Database Entity (with password hashes or internal IDs) directly to the client.

  • Automate Documentation: Ensure your DTOs are decorated for @nestjs/swagger. This turns your DTOs into a living API contract that frontend teams (and AI agents) can read easily.

Choosing a Validation Library for NestJS

Choosing between class-validator and Zod often comes down to your project’s architecture and how much you value code sharing.

Class-Validator

  • Syntax: Uses Decorators and Annotations directly on your class properties.

  • Type Inference: Mostly manual; you must define the class and the types separately.

  • Frontend Sharing: Difficult, as it relies on NestJS-specific decorators that don’t always play nice with frontend frameworks like React or Vue.

  • Complexity: Simple and intuitive for those used to Object-Oriented Programming.


Zod

  • Syntax: Uses a Functional Schema approach to define data shapes.

  • Type Inference: Automatic; the TypeScript types are inferred directly from your schema definitions.

  • Frontend Sharing: Seamless, making it the “gold standard” for monorepos where the same schema validates both your frontend forms and backend API.

  • Complexity: Highly Powerful, offering advanced features like data transformation and complex refinements.

By mastering DTOs, you aren’t just “fixing types”—you’re building a secure, predictable, and scalable gateway for your entire application.

Quote of the Day:

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”John Woods

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }

In the pursuit of technical mastery, we often find that the “standard” way of doing things eventually becomes the very thing holding us back. For those of us building complex, multi-tenant systems—platforms designed to challenge the status quo of content management—the traditional NestJS validation stack can start to feel like a collection of fragile abstractions.

If you are engineering a system where data integrity is the bedrock of “Agentic Commerce,” you need a validation strategy that is as disciplined as it is flexible. It is time to move beyond the decorator-heavy world of class-validator and embrace a Zod-first architecture. This shift isn’t just about a library change; it’s about establishing a single source of truth that spans your entire monorepo, from the deep backend to the storefront editor.


The Architecture of Certainty

When building a modern CMS, you aren’t just handling strings and numbers; you are handling the lifeblood of a merchant’s business. Traditional validation often leads to a “double-entry” problem: you define a class for the runtime and an interface for the compiler. This redundancy is where bugs thrive.

Zod eliminates this friction. By making the schema the foundation, you gain a functional, declarative contract that ensures your data is exactly what you think it is, exactly when you need it to be.

1. Defining the Shared Contract

In a monorepo, your validation logic should live in a shared library. This ensures your Next.js dashboard and NestJS API are always in perfect sync.

// apps/shared/schemas/content.schema.ts
import { z } from 'zod';

// The single source of truth for a CMS Post
export const PostSchema = z.object({
  title: z.string().min(5).max(100),
  slug: z.string().toLowerCase().regex(/^[a-z0-9-]+$/),
  content: z.record(z.any()), // Flexible for block-based editors
  status: z.enum(['draft', 'published', 'archived']),
});

// TypeScript inference: No manual interface needed
export type PostDto = z.infer<typeof PostSchema>;

2. The Validation Engine

To integrate this into NestJS, we implement a custom pipe. This replaces the “magic” of the global ValidationPipe with explicit, predictable logic.

// apps/backend/src/common/pipes/zod-validation.pipe.ts
import { PipeTransform, BadRequestException } from '@nestjs/common';
import { ZodSchema } from 'zod';

export class ZodValidationPipe implements PipeTransform {
  constructor(private schema: ZodSchema) {}

  transform(value: unknown) {
    const result = this.schema.safeParse(value);
    
    if (!result.success) {
      // Return structured errors perfect for a CMS dashboard UI
      throw new BadRequestException(result.error.format());
    }
    
    return result.data;
  }
}

3. Clean Controllers

With the pipe in place, your controllers become clean and focused on orchestration rather than data checking.

// apps/backend/src/modules/posts/posts.controller.ts
import { Controller, Post, Body, UsePipes } from '@nestjs/common';
import { PostSchema, PostDto } from '@shared/schemas/content.schema';
import { ZodValidationPipe } from '../../common/pipes/zod-validation.pipe';

@Controller('posts')
export class PostsController {
  
  @Post()
  @UsePipes(new ZodValidationPipe(PostSchema))
  async create(@Body() createPostDto: PostDto) {
    // createPostDto is 100% validated and typed here
    return this.postsService.create(createPostDto);
  }
}

4. Guarding the Exit: Response Serialization

In a multi-tenant environment, what you don’t send is as important as what you do. Zod allows you to explicitly shape your outgoing data.

// apps/backend/src/modules/posts/posts.service.ts

async getPublicPost(slug: string) {
  const rawData = await this.db.post.findUnique({ where: { slug } });
  
  // Strip internal tenant IDs or metadata before it hits the storefront
  const PublicPostSchema = PostSchema.extend({ id: z.string() });
  
  return PublicPostSchema.parse(rawData);
}

The Mamba Mentality in Code

Refactoring a core system to a Zod-first approach is an act of engineering discipline.

It is about stripping away the “magic” and replacing it with mastery.

By decoupling your validation from the framework and centering it around your data contracts, you build a system that is prepared for the scale of tomorrow.

Whether you are building the next generation of e-commerce or a decentralized content engine, your foundation is only as strong as your types.

Embrace the schema, eliminate the redundancy, and keep building.

What part of your stack will you refactor next?

Quote of the Day:

“First, solve the problem. Then, write the code.”John Johnson

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }