Kod Konvansiyonları ve Standartlar
İsimlendirme, veri çekme (loader/action, GraphQL vs REST), yetkilendirme, i18n, stil (Tailwind, erişilebilirlik), TypeScript.
Bu sayfa Coursio projesinde ekip içi tutarlılık için kullanılan kod konvansiyonlarını özetler. Yeni geliştiricinin “nasıl yazmalıyım?” sorusuna yanıt vermeyi hedefler.
İsimlendirme
Section titled “İsimlendirme”Bileşenler ve dosyalar
Section titled “Bileşenler ve dosyalar”- React bileşenleri: PascalCase (örn.
CourseCard.tsx,Navbar.tsx,InstructorSidebar.tsx). - Route dosyaları: React Router convention — path segment’leri nokta ile birleştirilir, parametre
$ile yazılır (örn.course.$id.tsx,instructor.course.$slug.manage.curriculum.tsx). Detay için Proje Yapısı — Route dosya isimlendirmesi. - API route dosyaları:
api.*.ts(örn.api.graphql.ts,api.stripe.webhook.ts). - Lib ve yardımcı modüller: kebab-case veya camelCase (örn.
db-queries.ts,pricing-engine.ts,graphql-client.ts,video-security.ts). - Hook’lar: camelCase,
useprefix (örn.useLang.ts,useLearningTracker.ts,useChatNotifications.ts).
Event handler’lar
Section titled “Event handler’lar”- Event handler fonksiyonları:
handleprefix (örn.handleClick,handleSubmit,handleLanguageChange,handleKeyDown). - Örnek:
onClick={handleClick},onSubmit={handleSubmit},onKeyDown={handleKeyDown}.
Route export’ları
Section titled “Route export’ları”- Veri çekme:
loaderexport edilir (async fonksiyon;request,params,contextalır). - Form / mutation:
actionexport edilir (async fonksiyon; form gönderimi ve API mutation’ları için). - Sayfa bileşeni: Default export bir React bileşeni.
Veri çekme: loader vs action, GraphQL vs REST
Section titled “Veri çekme: loader vs action, GraphQL vs REST”Sayfa verisi — loader
Section titled “Sayfa verisi — loader”- Sayfa açıldığında ihtiyaç duyulan veri loader ile çekilir.
- Loader,
context.cloudflare?.envveyagetDbUrl(env)ile DB bağlantısı alır; gerekirseinitAuthile session kontrolü yapar. - Veri
useLoaderData()ile bileşende kullanılır. - Çoğu sayfa verisi (kurs listesi, kullanıcı bilgisi, müfredat, admin listeleri vb.) GraphQL üzerinden de çekilebilir; sayfa loader’da
graphqlClientile query atılır veya doğrudan Drizzle sorguları kullanılır.
Form / mutation — action
Section titled “Form / mutation — action”- Form gönderimi, dosya yükleme, durum güncelleme (örn. onay/red) action ile işlenir.
- Action,
request.formData()veya JSON body ile veri alır; yetki kontrolü yapar; DB/API günceller; redirect veya JSON döner. - Upload, webhook, download gibi endpoint’ler REST/action kullanır:
api.lesson-resource-upload.ts,api.chat-image-upload.ts,api.stripe.webhook.ts,api.certificate.$id.download.ts,api.invoice.$enrollmentId.download.ts— bunlar sayfa değil, API route’u;loaderve/veyaactionexport edilir.
Ne zaman GraphQL, ne zaman REST/action?
Section titled “Ne zaman GraphQL, ne zaman REST/action?”| Kullanım | Tercih | Açıklama |
|---|---|---|
| Sayfa verisi (liste, detay, profil) | GraphQL veya loader içinde Drizzle | Çoğu sayfa verisi GraphQL query ile; bazı route’lar doğrudan Drizzle kullanır. |
| Form gönderimi, buton ile tetiklenen işlem | action (form action veya fetch) | Mutation GraphQL veya doğrudan API route’a POST. |
| Dosya yükleme | REST action | api.lesson-resource-upload, api.chat-image-upload — FormData, multipart. |
| Webhook | REST action | api.stripe.webhook — Stripe imza doğrulama, raw body. |
| PDF/ dosya indirme | REST loader | api.certificate.$id.download, api.invoice.$enrollmentId.download — GET, Response döner. |
Yetkilendirme
Section titled “Yetkilendirme”Session kontrolü nerede yapılır?
Section titled “Session kontrolü nerede yapılır?”- Layout loader’da yapılır. Örneğin:
- admin.tsx:
auth.api.getSession({ headers: request.headers }); session yoksa veyauser.role !== "admin"iseredirect("/404"). - instructor.tsx: Session + eğitmen kaydı / rol kontrolü.
- account.tsx, my-courses.tsx: Session yoksa giriş sayfasına yönlendirme.
- admin.tsx:
- Alt sayfalar layout’tan geçtiği için ayrıca session kontrolü yazmaya gerek yok; gerekirse sayfa loader’da ek kontrol yapılabilir.
Rol pattern’i
Section titled “Rol pattern’i”- Admin:
session?.user?.idve veritabanındanuser.role === "admin"kontrolü (örn.admin.tsxloader). - Eğitmen:
user.role === "instructor"veya eğitmen kaydı tamamlanmış kullanıcı; instructor layout ve ilgili API route’larında kontrol. - Öğrenci / giriş gerekli: Sadece session varlığı; rol kontrolü yok (veya sayfa bazında “sadece enrollment sahibi” gibi kısıtlar).
API route’larında yetki
Section titled “API route’larında yetki”- Her API route kendi içinde session alır:
initAuth(dbUrl, env)→auth.api.getSession({ headers: request.headers }). - Gerekirse
session?.user?.role === "admin"veya"instructor"kontrolü yapılır; yetkisiz ise 401/403 dönülür.
i18n (çoklu dil)
Section titled “i18n (çoklu dil)”- Hook:
useTranslation()(react-i18next);const { t } = useTranslation(); - Metin:
t("key")veyat("namespace:key")— örn.t("footer.about"),t("nav.language"). - Çeviri dosyaları:
app/locales/*.json—tr.json,en.json,de.json,es.jsonvb. Her dosyada aynı key yapısı kullanılır. - Yeni key ekleme: Tüm dil dosyalarına aynı key’i ekleyin; eksik dilde fallback için varsayılan dil (örn.
en) kullanılabilir. - Dil ve path:
useLang(),getLocalizedPath("/path")— link’lerde dil prefix’li path üretmek için. Dil değiştirme:lib/i18n-utils.ts—changeLangInPath,getLangFromPath,shouldIncludeLang.
Stil (Tailwind, erişilebilirlik)
Section titled “Stil (Tailwind, erişilebilirlik)”- Tailwind: Tüm stil Tailwind sınıfları ile verilir;
className="...". Mümkün olduğunca global CSS veya inline style kullanılmaz. - Koşullu sınıf:
class:(classList mantığı) veya ternary kullanılır. Ekip tercihi: okunabilir olduğu sürececlassName={cn("base", condition && "active")}veyaclassName={condition ? "a" : "b"}. - Erişilebilirlik (a11y):
- Etkileşimli öğelerde
aria-label(örn. ikon butonları:aria-label={t("nav.language")}). - Klavye ile erişim için
tabIndex={0}(gerektiğinde);onKeyDownile Enter/Space ile tetikleme. - Form alanlarında
labelveyaaria-labelledbykullanılır.
- Etkileşimli öğelerde
TypeScript
Section titled “TypeScript”- Context tipi: Route loader/action’da
contextgenelde{ cloudflare?: { env?: unknown } }veya proje tipi ile tanımlanır.envkullanımı:const env = (context?.cloudflare?.env as any) || process.env;(yereldeprocess.envfallback). - Veritabanı:
getDb(env.DATABASE_URL)veyagetDb(getDbUrl(env))—app/db/index.ts. Hyperdrive kullanıldığında production’da env üzerinden Hyperdrive bağlantısı sağlanır. - Auth:
initAuth(dbUrl, env)—app/lib/auth.ts; session tipi Better Auth tarafından tanımlanır;session?.user?.id,session?.user?.rolekullanımı yaygındır. - Genel: Mümkün olduğunca
anyyerine net tipler kullanılır; React Router ve Drizzle şemaları projede tip güvenliği sağlar.
| Konu | Kural |
|---|---|
| İsimlendirme | Bileşenler PascalCase; route dosyaları React Router convention ($param, api.*.ts); event handler’lar handle*; loader/action export. |
| Veri | Sayfa verisi → loader (GraphQL veya Drizzle); form/mutation → action; upload/webhook/download → REST action/loader. |
| Yetki | Layout loader’da session ve rol kontrolü; session?.user?.role === "admin" / "instructor"; API route’larında kendi session kontrolü. |
| i18n | useTranslation(), t("key"); çeviriler app/locales/*.json; yeni key tüm dillere eklenir; getLocalizedPath, useLang. |
| Stil | Tailwind className; koşullu sınıf class: veya ternary; aria-label, tabIndex, onKeyDown, label kullanımı. |
| TypeScript | context.cloudflare?.env; getDb(env.DATABASE_URL); initAuth(dbUrl, env); net tipler tercih edilir. |
Detaylı route ve dosya yapısı için Proje Yapısı (Detaylı) ve Route Haritası sayfalarına bakın.