nestjsvalidationdtotypescript

NestJS Validation and DTO Boundaries

Validation in NestJS works best when DTOs describe transport input, services own business rules, and transformation is deliberate.

2 min readProduction checklistSaeed Ghofrani Ivari

From my notebook

Make the HTTP boundary honest

Where this came from

I keep returning to this subject because validation bugs often arrive disguised as database or service errors. The fix is usually to make the HTTP boundary honest: query parameters are strings, frontend types are not runtime guarantees, and DTOs should not double as persistence models.

My short checklist

  • - DTOs describe transport input, not the whole domain model.
  • - Requests are runtime data even when the frontend uses TypeScript.
  • - Normalize query and route values near the controller boundary.
  • - Test bad inputs, not only happy paths.

Validation problems in NestJS usually come from mixing three different concerns: transport input, domain rules, and persistence shape. A DTO should describe what the API accepts. It should not become the database model, the domain entity, and the UI contract at the same time.

Remember that requests are strings first

Query parameters arrive as strings. Route params arrive as strings. JSON bodies can contain unexpected types even when the frontend is typed. TypeScript does not protect runtime boundaries. If transformation is enabled, it should be enabled deliberately and tested with real HTTP-shaped values, not only with already-created class instances.

Keep DTOs explicit

I prefer DTOs that make required fields, optional fields, nested objects, and arrays obvious. If an endpoint accepts a filter object, the allowed filter fields should be visible in the DTO instead of passed as a loose object. This protects the database layer from accidental broad queries and makes API behavior easier to document.

  • DTOs validate transport input.
  • Services enforce business rules.
  • Repositories shape database access.
  • Response serializers control what leaves the system.

Do not hide conversion in services

If page and limit are strings at the controller boundary, converting them deep inside a service makes behavior harder to reason about. The controller layer or a dedicated pipe should normalize transport values before business logic runs. Then services can work with real numbers, dates, booleans, and enums.

Test the bad inputs

Happy-path validation tests are not enough. I test missing fields, wrong types, empty strings, oversized arrays, invalid enum values, and nested object failures. These cases catch the bugs that otherwise show up as database errors, confusing 500 responses, or silent incorrect filters.

One useful check is an HTTP request with page=abc, an empty identifier, or an unexpected nested field. The API should reject it at the boundary instead of passing an invalid value into a query.