API'ma Bir Veritabanı Bağladım — ve Yanlışlıkla Bir Şifre Sızdırdım
3. Günün sonunda şöyle demiştim: “Şu an API’m hep aynı sabit cevabı veriyor, gerçek bir veri yok. Sıradaki adım, ona gerçek bir veritabanı bağlamak.” Bugün tam olarak bunu yaptım — ve günün sonunda, kendi elimle az kalsın bir şifreyi internete sızdırıyordum. Nasıl olduğunu, adım adım anlatıyorum.
Önce üç kavram: Veritabanı, tablo, ORM
Veritabanı, düzenli tutulan bir bilgi arşivi. Bilgisayarında dosyaları rastgele bir klasöre atmak yerine, her bilginin belirli bir yerde, belirli bir kurala göre durduğu bir sistem düşün. API’m şu ana kadar hiçbir şeyi hatırlamıyordu; her istek geldiğinde aynı sabit cevabı veriyordu, çünkü hatırlayacağı bir yer yoktu.
Tablo, bir Excel sayfası gibi düşünülebilir. Her satır bir kayıt (benim durumumda: bir blog yazısı), her sütun o kaydın bir özelliği (başlık, içerik, yayınlanma tarihi gibi). Bugün posts adında bir tablo oluşturdum, sütunları da: başlık, kısa URL adı (slug), içerik, özet, yayında mı değil mi bilgisi ve tarihler.
ORM (Drizzle adlı araç bunu yapıyor), benim yazdığım kodla veritabanının anladığı dil (SQL) arasında çeviri yapan bir çevirmen. Ben “bana yayında olan tüm yazıları getir” gibi bir şey yazıyorum, Drizzle bunu veritabanının anlayacağı hale çeviriyor. SQL’i hiç bilmeden bugün bir veritabanıyla konuşabildim, sebebi bu.
Neon: Veritabanımın yaşadığı yer
Tıpkı sitemin Cloudflare’de, kodumun GitHub’da yaşaması gibi, bir veritabanının da bir yerde durması, 7/24 açık ve erişilebilir olması lazım. Neon, bunu benim için yapan bir şirket — Postgres denen (dünyada en çok kullanılan veritabanı sistemlerinden biri) bir veritabanını, ben hiç sunucu kurmadan, hazır bana veriyor. Neon panelinde birkaç tıkla bir proje açtım, arka planda benim için bir veritabanı ayağa kalktı.
Sekizinci hata: Paketleri kurduğumu sandım, kurmamışım
Drizzle’ı projeme eklemek için terminale bir kurulum komutu yazmam gerekiyordu. Ama yanlışlıkla parametresiz, yani sadece npm install yazdım. Bu komut, “zaten listede olan paketleri kur” demek — yeni bir paket eklemiyor. Ben ise “drizzle’ı da listeye ekle” demek istiyordum, bunun için paketin ismini de yazmam gerekiyordu: npm install drizzle-orm @neondatabase/serverless. Terminal bana “her şey güncel” dedi, ben de her şeyin yolunda olduğunu sandım — oysa istediğim paket hiç kurulmamıştı. Fark etmemin sebebi, birkaç adım sonra o paketleri kullanan bir kod çalıştırmaya çalışıp hata almamdı.
Dokuzuncu hata: Gizli şifre dosyamı kimse okumuyormuş
Veritabanının adresini ve şifresini (buna “connection string” deniyor, yani “bağlantı adresi”) .env.local adında bir dosyaya yazdım. Bu dosya, git’e hiç gitmeyen, sadece benim bilgisayarımda duran gizli bir not defteri gibi.
Sonra Drizzle’a “şu tabloyu gerçek veritabanında oluştur” dedirtmek için bir komut çalıştırdım. Ama şu hatayı aldım:
Either connection "url" or "host", "database" are required for PostgreSQL database connection
Yani Drizzle’ın kullandığı araç, bağlantı adresini bulamıyordu — çünkü .env.local dosyasını kendiliğinden okumuyormuş. Bunu ancak bir başka küçük araç (dotenv adında) ona söylerse okuyordu. O aracı kurup, “önce şu dosyayı oku, sonra işine bak” diye bir satır ekledim. Sorun çözüldü.
Burada öğrendiğim şey şuydu: bir dosyaya bir bilgiyi yazmak, o bilginin otomatik olarak her yerde kullanılacağı anlamına gelmiyor. Her araç, kendine söylenmediği sürece, o dosyanın varlığından bile haberdar değil.
Yeni bir kavram: Aynı şifre, üç ayrı hafıza
Burada işler biraz karıştı, çünkü öğrendim ki API’m çalıştığı yer (Cloudflare Workers) normal bir bilgisayar gibi davranmıyor — kendine has kuralları var. Bunun sonucunda, aynı veritabanı şifresini üç ayrı yere, üç ayrı sebeple yazmam gerekti:
.env.local— sadece veritabanı tablosunu düzenleyen araç (Drizzle) için. Bu araç benim bilgisayarımda, normal bir programmış gibi çalışıyor..dev.vars— API’mi kendi bilgisayarımda test ederken (localhostadresinde) kullandığım kopyası için.- Cloudflare’in “secret” dediği, şifreli bir kasa — API canlıya çıktığında, gerçek dünyada çalışırken kullandığı kopya. Bu, bir dosya bile değil; komutla (
wrangler secret put) Cloudflare’in sunucularına direkt, şifreli olarak yüklüyorsun.
Yani üç ayrı “hayat evresi” (benim bilgisayarımda araç çalıştırmak, benim bilgisayarımda API’yi test etmek, gerçek dünyada API’yi çalıştırmak) için üç ayrı hafıza var, ve hiçbiri diğerinden otomatik haberdar değil. Bu, günün ilerleyen kısmında başıma gelecek en büyük hatanın da tohumunu attı.
Onuncu hata: Sessiz bir “Sunucu Hatası” ve arkasındaki şifre sızıntısı
API’mi Cloudflare’e yükleyip canlı adresten /api/posts’u açtığımda, karşıma sade bir “Internal Server Error” (Sunucu Hatası) çıktı. Hiçbir detay yok, sadece “bir şeyler ters gitti” diyordu. Local’de (kendi bilgisayarımda) sorunsuz çalışıyordu, demek ki sorun canlı ortama özgüydü.
Hatayı görünür kılmak. Karanlıkta tahmin yürütmek yerine, API’ma geçici olarak şunu yaptırdım: bir şey ters giderse, hatanın gerçek mesajını, cevabın içinde bana göster. Ayrıca, gizli şifrenin kendisini hiç açığa çıkarmadan, sadece “kaç karakter uzunluğunda, ilk harfi ne, son harfi ne” gibi zararsız ipuçları veren ayrı bir kontrol noktası ekledim — tıpkı bir zarfın içini açmadan, tartıp kalınlığına bakmak gibi.
Bu ipucu şunu gösterdi: şifrem postgresql:// ile başlaması gerekirken D harfiyle başlıyordu, sonu da bir tırnak işaretiyle bitiyordu. Yani ben Cloudflare’in kasasına (secret’a) sadece adresin kendisini değil, .dev.vars dosyasındaki satırın tamamını — DATABASE_URL="postgresql://..." yazısını, tırnaklarıyla ve etiketiyle birlikte — olduğu gibi yapıştırmıştım. Kasa da bunu, geçersiz bir adres olarak kaydetmişti.
Asıl can sıkıcı kısım burada başlıyor. Sorunu görünür kılmak için eklediğim o “hatanın gerçek mesajını göster” özelliği, düzeltmeden önceki birkaç dakika boyunca canlıda, herkese açık bir adreste durdu. Ve bağlantı denemesi başarısız olduğunda fırlatılan hata mesajı, içinde denenen adresin tamamını — yani şifremi de — taşıyordu. Yani o birkaç dakikalık pencerede, /api/posts adresine giden herhangi biri, teorik olarak veritabanımın şifresini düz yazıyla görebilirdi.
Kimsenin görmüş olma ihtimali düşük — adres yeni, kimse bilmiyor, tesadüfen denk gelmesi gerekiyordu. Ama “muhtemelen kimse görmedi” bir güvenlik stratejisi değil. İnternette bir şey bir kez görünürse, onu görmediğini kimse garanti edemez: bir tarayıcı, bir güvenlik botu, bir önbellek — hangisinin denk gelip almadığını asla bilemezsin. Bu yüzden riski sıfıra indirmenin tek yolu şifreyi geçersiz kılmaktı.
Şifreyi değiştirmek ve üç yeri yeniden yazmak
Neon panelinden veritabanı şifremi sıfırladım — bu, bir kapının kilidini değiştirmek gibi bir şey, eski anahtar (eski şifre) artık hiçbir işe yaramıyor. Sonra üç hafızayı da (.env.local, .dev.vars, Cloudflare’in kasası) yeni şifreyle, bu sefer sadece adresin kendisini, tırnaksız ve etiketsiz olarak güncelledim. Her birini tek tek test ettim, hepsi yeni şifreyle sorunsuz çalıştı.
Küçük bir korkulu rüya: Türkçe karakterler bozuk görünüyordu
Testler sırasında API’dan gelen cevapta yazım şöyle görünüyordu: Merhaba Dünya. Bir an “veritabanına yanlış kaydetmişim” diye paniklemiştim. Ama sebebi bambaşkaydı: kullandığım terminal programı (PowerShell), Türkçe harfleri (ü, ş, ö gibi) doğru göstermekte zaman zaman zorlanıyor — bu bir görüntüleme sorunu, verinin kendisinde bir bozukluk yok. Aynı adresi normal bir tarayıcıda açtığımda “Merhaba Dünya” tamamen doğru görünüyordu. 2. Günde Cloudflare’in Pages’ten Workers’a isim değiştirmesinde yaşadığım gibi, burada da “hata sandığım şey” aslında hiç hata değildi — sadece yanlış yerden bakıyordum.
Sonunda çalıştı: API artık gerçek veri döndürüyor
Şifre düzelince, API’ma iki basit kapı ekledim: biri tüm yayınlanmış yazıları listeleyen, biri de tek bir yazıyı adıyla (slug) getiren. Neon’un kendi arayüzünden elle bir test yazısı ekledim, sonra bu iki kapıyı da hem kendi bilgisayarımda hem canlıda test ettim — ikisi de doğru veriyi döndürdü, olmayan bir yazıyı istediğimde de düzgün bir “bulunamadı” cevabı verdi. En son, hatayı görünür kılmak için eklediğim geçici kontrol noktalarının hepsini kaldırıp API’yi son kez, temiz haliyle yayına aldım.
Bugünden çıkardığım büyük resim
Bugünün hataları, önceki günlerden farklıydı. Önceki günlerde sorun genelde “yanlış komut yazdım” ya da “yanlış klasördeydim” gibi somut şeylerdi. Bugünse asıl ders, farklı ortamların (kendi bilgisayarım, canlı dünya) birbirinden habersiz olduğunu ve bir gizli bilgiyi (şifre) taşırken, geçici bile olsa, dışarıya açık bir yerden asla geçirmemem gerektiğini öğrenmekti. Elektronik mühendisliğinden gelen biri olarak şöyle düşünüyorum: bir devrede topraklama hattını yanlış yere bağlarsan devre bazen çalışır gibi görünür ama aslında güvenli değildir. Bugün yaşadığım da tam olarak buydu — sistem “çalışıyor” görünene kadar sorunun farkına bile varmadım.
Kısaca Bir özet geçelim
Süreç Kontrolü özetle şu şekilde . İstek & Yönlendirme: Hono.js ile gelen istekleri karşılayan API’yi yazdık.
Çeviri & Sorgu: Drizzle kullanarak kodunu SQL’e çevirdin ve veritabanından veriyi çektik. Cevap: API bu veriyi alıp JSON formatında istemciye (ekrana) bastı.
ORM bir yöntem/tekniktir. Açılımı Object-Relational Mapping’dir. Kodumuzdaki nesneler ile veritabanımızdaki tabloları birbirine bağlama tekniğidir.
Drizzle ise bu yöntemi uygulayan kütüphanenin/aracın adı.
Önceden bahsettiğimiz Edge Computing nasıl bir mimarî/yaklaşım ise, ORM da öyle bir yöntemdir. Cloudflare nasıl bir araçsa, Drizzle da bir ORM aracıdır.
Sırada ne var?
Veritabanım artık gerçek veri tutuyor ve API’m bu veriyi güvenli şekilde dışarı veriyor. Ama şu an yazıları elle, Neon’un kendi arayüzünden ekliyorum — bu uzun vadede sürdürülebilir değil. Sıradaki adım, blogumun ön yüzünü (frontend’i) bu API’ye bağlamak: artık blog yazılarımın Astro’nun elle yazdığım dosyalarından değil, doğrudan bu veritabanından okunmasını sağlamak.
Yorumlar