Validating Domain Models
Lesson: Validating Domain Models and Query Parameters
Welcome back 👋 In the previous lesson, you laid the groundwork for a consistent backend by defining standard success and error envelopes and enforcing them across all API routes. Every endpoint now speaks the same language, whether it succeeds or fails.
In this lesson, we build directly on that foundation. You’ll learn how to validate incoming data early, define clear domain models and DTOs, and safely handle common inputs like query parameters. By the end, your /api/products endpoint will still return an empty list—but it will do so only after carefully validating pagination and search inputs, using reusable validation helpers and strongly typed models.
Previously: Establishing a Consistent API Contract
Previously, you focused on how the API responds rather than what it returns. You introduced:
- A shared
ApiSuccess/ApiErrorcontract - Centralized
success()anderror()helpers - Predictable response envelopes for all routes, including stubbed ones
That work matters here because validation failures are just another kind of response. Thanks to that foundation, invalid input can now be rejected cleanly and consistently, without special cases or ad-hoc JSON.
Why Validation and Domain Models Matter
As this backend grows, it will support products, shopping carts, orders, and state transitions like paying or canceling an order. Each step introduces more data, more relationships, and more opportunities for invalid input.
Validation is how you protect the system’s core rules:
- Query parameters must be well-formed and bounded.
- Identifiers must look like real IDs.
- Domain entities must have a single, authoritative shape.
- Clients must only be allowed to send what they’re responsible for.
This lesson introduces the building blocks that make those guarantees possible.
The Product Domain Model: The Backend’s Source of Truth
A backend needs a canonical definition of what a “product” is. This definition lives in src/lib/types/domain.ts and represents the shape the backend itself trusts—typically mirroring database rows.
Product domain type:
This file defines the authoritative product model used throughout the backend.
- This interface represents a complete product, including system-managed fields like
id, timestamps, andstatus. - Monetary values are stored as integers in cents (
price_cents) to avoid floating-point rounding issues. CurrencyandProductStatusare constrained unions, which prevents invalid values from entering the system.- This type is what the backend and database agree on—it is not what clients are allowed to send directly.
