Дані змінюють форму, коли перетинають межу застосунку: з JSON у внутрішню модель (рядок → Date, копійки → об'єкт грошей, snake_case → camelCase) і назад - при відправці на сервер. transform у схемі описує лише один напрямок; зворотне перетворення доводиться писати окремо, і два описи розходяться.
Кодек (z.codec, з Zod 4.1) описує обидва напрямки в одному місці:
import * as z from 'zod';
const isoDatetimeToDate = z.codec(
z.iso.datetime({ offset: true }), // вхід: рядок ISO з JSON
z.date(), // вихід: Date у застосунку
{
decode: (iso) => new Date(iso),
encode: (date) => date.toISOString(),
},
);
const EventSchema = z.object({
title: z.string(),
startsAt: isoDatetimeToDate,
});
const event = z.decode(EventSchema, json); // startsAt: Date
const payload = z.encode(EventSchema, editedEvent); // startsAt: string для відправки
decode- з «дротового» формату у внутрішній (якparse);encode- зворотно, з перевіркою, що результат відповідає вхідній схемі.
Де це корисно:
- дати й час - рядки ISO в JSON,
DateчиTemporalу коді; - гроші -
decimalз Laravel приходить рядком"125.50", у застосунку - ціле число копійок чи об'єкт з валютою; - ідентифікатори - числа в JSON, брендовані типи в коді;
- JSON у рядку (поле
metaяк рядок) - розбір і зворотна серіалізація; - параметри URL - рядки ↔ числа, булеві, масиви.
Альтернативи без кодеків: окремі функції fromApi() і toApi() у шарі API-клієнта. Працює, але вимагає дисципліни - кожне нове поле треба не забути додати в обидві функції. Кодек робить пропуск помітним: схема одна.
Принципи роботи з межею:
- перетворювати один раз - в API-клієнті, а не в компонентах;
- внутрішня модель не мусить збігатися з форматом API: зручні назви, правильні типи, без полів, які інтерфейсу не потрібні;
- зміни формату API тоді торкаються лише одного місця - схеми на межі.
Обмеження: не кожне перетворення має обернене (обрізання пробілів, втрата точності) - для таких лишається однобічний transform.