Create ve Update Şemalarını Tek Bir Kaynaktan Türetmek
packages/schemas'daki her kaynak (post, project) aynı üç adımlı deseni izliyor: taban şema, ondan .omit() ile türetilen create şeması, create'ten .partial() ile türetilen update şeması.
export const postSchema = z.object({
id: z.number().int().positive(),
slug: z.string().min(1).max(200).regex(/^[a-z0-9-]+$/),
titleTr: z.string().min(1).max(200),
// ...
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
});
export const createPostSchema = postSchema.omit({ id: true, createdAt: true, updatedAt: true });
export const updatePostSchema = createPostSchema.partial();
.omit() neden create şemasında
Bir kaynak oluştururken id, createdAt, updatedAt gibi alanlar client'tan gelmiyor — veritabanı/sunucu tarafından üretiliyor. .omit(), taban şemadan bu üç alanı çıkarıp geri kalanının aynı kısıtlarla (min/max/regex) geçerli kalmasını sağlıyor. Create şemasını sıfırdan elle yazmak, taban şemadaki bir kısıt değiştiğinde (örneğin slug maksimum uzunluğu) iki yerde senkron tutulması gereken bir tekrar yaratırdı.
.partial() neden update şemasında
PUT/PATCH bir kaynağın sadece değişen alanlarını göndermeyi mümkün kılmalı — tüm alanları zorunlu tutan bir şema, "sadece başlığı güncellemek istiyorum" gibi kısmi güncellemeleri imkansız kılardı. .partial(), create şemasındaki her alanı opsiyonel yapıyor ama kısıtları (bir alan gönderilirse hâlâ regex/max kontrolünden geçmesi) korumaya devam ediyor.
Zincirin tek yönlü olması neden önemli
updatePostSchema, createPostSchema'dan; createPostSchema de postSchema'dan türetiliyor — tersi değil. Bu, gerçek kaynağın (taban şema, veritabanındaki gerçek şekli yansıtan) tek olduğunu ve diğer ikisinin ondan "daha az kısıtlayıcı" varyantlar olduğunu netleştiriyor. Taban şemaya yeni bir zorunlu alan eklendiğinde, createPostSchema onu otomatik miras alıyor (aksi belirtilmedikçe) — üç şemayı ayrı ayrı güncellemeyi unutma riski ortadan kalkıyor.