Type-Safe API Nedir ve Neden Hayat Kurtarır?
Modern web mimarisinde frontend (istemci) ve backend (sunucu) birbirleriyle sürekli JSON objeleri aracılığıyla haberleşir. Ancak klasik JavaScript ile geliştirilmiş sistemlerde, backend'in döndüğü verinin yapısı değiştiğinde frontend tarafı bundan haberdar olmaz ve üretim ortamında (production) ölümcül undefined hataları alınır.
TypeScript, uçtan uca (End-to-End) tip güvenliği sağlayarak, verinin nasıl bir yapıda (Interface / Type) olacağını derleme aşamasında garantiler.
Interface Kullanımı
Öncelikle hem istemcinin hem de sunucunun bileceği ortak bir sözleşme (contract) hazırlamamız gerekir. Bunu types/user.ts adlı bir dosyada tutalım:
// Ortak Tip Sözleşmesi
export interface UserResponse {
id: string;
username: string;
email: string;
isActive: boolean;
role: 'ADMIN' | 'USER' | 'MODERATOR';
lastLoginAt?: Date; // Opsiyonel alan
}
export interface ErrorResponse {
status: number;
message: string;
code: string;
}
Sunucu (Backend) Tarafı: Express + TS
Express.js kullanırken response objesine jenerik tipler atayarak, API'ın yalnızca bizim belirlediğimiz formatta veri dönmesini zorunlu kılabiliriz.
import { Request, Response } from 'express';
import { UserResponse, ErrorResponse } from '../types/user';
// Sadece UserResponse veya ErrorResponse dönebilir
export const getUser = async (
req: Request,
res: Response<UserResponse | ErrorResponse>
) => {
try {
const userId = req.params.id;
const user = await db.users.findById(userId); // Örnek DB çağrısı
if (!user) {
return res.status(404).json({
status: 404,
message: "Kullanıcı bulunamadı",
code: "USER_NOT_FOUND"
});
}
// TypeScript burada dönen verinin UserResponse formatına uyup uymadığını denetler.
// Eğer username yerine firstName dönerseniz, VSC'de kırmızı alt çizgi ile hata alırsınız.
return res.status(200).json({
id: user.id,
username: user.username,
email: user.email,
isActive: user.isActive,
role: user.role
});
} catch (error) {
return res.status(500).json({ status: 500, message: "Sunucu hatası", code: "SERVER_ERROR" });
}
};
İstemci (Frontend) Tarafı: Fetch İşlemi
Artık frontend tarafında, API'dan gelen cevabın ne olduğunu %100 biliyoruz. Otomatik tamamlama (IntelliSense) sayesinde data. yazdığımızda tüm özellikler karşımıza çıkacak.
import { UserResponse } from '../types/user';
async function fetchUserData(userId: string): Promise<UserResponse> {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Kullanıcı verisi alınamadı');
}
// Burada gelen JSON verisini UserResponse tipine cast ediyoruz
const data = await response.json() as UserResponse;
return data;
}
// Kullanım:
fetchUserData('123').then(user => {
console.log(user.username); // Type-safe ve otomatik tamamlama çalışır
});
Eğer yukarıdaki gibi manuel tip ataması (Type Casting) yapmak istemiyorsanız, çalışma zamanında (runtime) veri doğrulama yapan Zod veya uçtan uca otomatik RPC çağırma sistemi sunan tRPC araçlarını öğrenmenizi şiddetle tavsiye ederiz.
