Learning Node.js - Models, Schemas, and Data Validation
Episode 15 of 23

Learning Node.js - Models, Schemas, and Data Validation

This episode brings together models, schemas, and validation: designing a database schema with Prisma, validating request input with Zod, and applying validation in Express routes before data touches the database.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

The data that enters an application comes from the outside world — forms, third-party APIs, user requests — and can never be trusted at face value. The layer that handles this is validation: checking data before it's processed or stored.

Episode 15 weaves together three complementary concepts: models for data structure in the database, schemas as a formal description of that structure, and validation as a runtime check of incoming data. You'll use Prisma for models and Zod for input validation in Express routes.

Designing Models and Schemas

The Source of Truth in Prisma

A good database model determines an application's long-term quality. The Prisma schema from episode 14 can be extended with relations — for example, one user has many articles:

Model with relations
model Pengguna {
  id        Int        @id @default(autoincrement())
  email     String     @unique
  nama      String
  artikel   Artikel[]
}
 
model Artikel {
  id         Int      @id @default(autoincrement())
  judul      String
  isi        String
  penulisId  Int
  penulis    Pengguna @relation(fields: [penulisId], references: [id])
}

The declaration artikel Artikel[] in the Pengguna model and penulis Pengguna @relation(...) in the Artikel model form a one-to-many relation. Migrations then translate this schema into tables. A good design prevents duplicate data and dangling relations.

Schema Design Principles

When designing a schema, ask: what are the core entities, what are their relations, and which fields are mandatory? Add indexes for columns frequently used as filters. A schema that's too rigid is hard to change, but one that's too loose creates messy data — balance is the key.

Validating Input Data with Zod

Describing the Data You Accept

Zod is a validation library popular in the TypeScript and Node.js ecosystems. You describe the expected data shape, and Zod checks real data against that description:

Install Zod
npm install zod

npm install zod adds a validator with automatic type inference. Zod integrates smoothly with Prisma and TypeScript, but also works without TypeScript.

Your First Validation Schema

JSValidation schema with Zod
import { z } from "zod";
 
const skemaPengguna = z.object({
  nama: z.string().min(3).max(100),
  email: z.string().email(),
  umur: z.number().int().positive().optional(),
});

z.object({...}) describes the expected object: nama must be a string with a length of 3 to 100, email must be in email format, and umur is optional, a positive integer. skemaPengguna.parse(data) throws an error if data violates the rules, while skemaPengguna.safeParse(data) returns a result without throwing.

Applying Validation in Routes

Validation Middleware

Combine the Zod schema with Express routes through middleware:

JSRoute with validation
app.post("/api/pengguna", (req, res) => {
  const hasil = skemaPengguna.safeParse(req.body);
 
  if (!hasil.success) {
    return res.status(400).json({
      error: "Data tidak valid",
      detail: hasil.error.issues,
    });
  }
 
  const pengguna = await prisma.pengguna.create({
    data: hasil.data,
  });
  res.status(201).json(pengguna);
});

skemaPengguna.safeParse(req.body) checks the request body. If it fails, a 400 response carries the validation issue details; if it succeeds, the clean data in hasil.data is passed to Prisma. This pattern guarantees the data reaching the database is already shaped per the schema.

Validating Before Touching the Database

The defense order matters: validate input at the edge of the application, not near the database. With route-level validation, bad requests are rejected earlier without wasting database resources. Prisma itself also validates on create, but earlier Zod validation means clearer, more controlled errors for API consumers.

Keeping Schema and Validation Consistent

One Description, Many Benefits

The Prisma model and the Zod schema describe the same data from two sides: one for persistence, one for the API boundary. Ideally both stay aligned — required fields in Zod exist in the model, the types used match, and allowed relations don't violate database rules.

Keeping both in sync requires discipline, especially as a team grows. Some projects derive the Zod schema directly from the Prisma model to avoid duplication, while others accept duplication for clarity. Choose the approach your team can sustain.

Closing

Here's what to take away:

  • Prisma models describe the database structure including relations.
  • A good schema design prevents duplicate data and dangling relations.
  • Zod validates runtime data with the z.object description.
  • safeParse returns a result without throwing an error.
  • Validation happens at the route, before data touches the database.
  • Keep the Prisma model and Zod schema aligned.

In the next episode, episode 16, we'll discuss caching, sessions, and server-side state — the difference between stateless and stateful applications, sessions with express-session, caching with Redis, and setting cache headers for HTTP responses. You'll learn to build a faster, more resource-efficient API.