Geçen haftayı .night domainlerinin gerçekte nasıl çalıştığını anlamaya çalışarak geçirdim. Bir teknik şartname üzerinden değil, doğrudan zincirle temas kuran bir şey inşa ederek. Sonuç, küçük bir profil görüntüleyici oldu. tomin.night gibi bir isim yazıyorsunuz ve profil doğrudan blokzincirinden çözümleniyor. Kayıt kuruluşu (registrar) API'si yok. Kimlik doğrulama duvarı yok. Sadece bir akıllı sözleşme ve biraz JavaScript.
Aşağıda bunun nasıl çalıştığı, neler öğrendiğim ve öğleden sonramı yiyip bitiren iki spesifik tuzak yer alıyor.
Zincir Üstü (On-Chain) İsimler Neden Önemlidir
Midnight üzerinde, .night domainleri Midnames tarafından yönetilir. Bir şirketin merkezi sunucusuna sorgu göndermek yerine, doğrudan zincir üzerinde yaşayan bir akıllı sözleşmeden okuma yaparsınız. Kayıt defterinin (registry) kendisi domaini, sahibini ve sahibinin eklediği tüm profil alanlarını tutar. Bu veriler zincir üstünde olduğu için kimlik taşınabilirdir. Şartları değiştirebilecek veya hizmeti durdurabilecek bir platformdan kiralama yapmıyorsunuz. Anahtarlar sizdeyse, isim de sizdedir.
Bu değişim geliştiriciler için de önemlidir. Geleneksel bir DNS sistemine karşı geliştirme yaparken hız sınırları (rate limits), API anahtarları ve çalışma süresi (uptime) sözleriyle uğraşırsınız. Burada ise sözleşme durumu (contract state) tek gerçeklik kaynağıdır (source of truth). Uygulamanız, onu diğer tüm uygulamaların okuduğu şekilde okur. Ayrıcalıklı bir API katmanı yoktur.
Kod Neredeyse Fazla Basit
@midnames/sdk ağır iş yükünü üstlenir. Bir domaini bir adrese ve profile çözümlemek tam olarak iki satır sürer:
const provider = createDefaultProvider({ networkId: "mainnet" });
const result = await resolveDomain(provider, "tomin.night");
İşte bu kadar. Sağlayıcı (provider) ağı hedefler ve çözücü (resolver) sözleşme ile konuşur. SDK, bir başarı bayrağı (success flag) içeren bir sonuç nesnesi döndürür. Eğer domain mevcut değilse, RPC hataları etrafında katmanlı hata yönetimine veya try-catch bloklarına ihtiyacınız olmaz. SDK, orada hiçbir şey olmadığını size net bir şekilde söyler. Bu, kullanıcı arayüzü (UI) oluşturmayı şaşırtıcı derecede keyifli hale getirir. Başarı bayrağına göre dallanma yapabilir ve hatanın eksik bir isimden mi yoksa ölü bir düğümden (node) mi kaynaklandığını tahmin etmeye çalışmadan bir "bulunamadı" durumu gösterebilirsiniz.
Geriye Ne Alırsınız
Arama başarılı olduğunda, veri yükü (payload) önemli olan iki parça içerir.
Target, domainin işaret ettiği cüzdan adresidir. Bu, temel işlevdir. Uzun bir hex adresini bir insanın okuyabileceği, yazabileceği ve hatırlayabileceği bir şeye dönüştürür.
Fields, profil detaylarını tutar. Sahibinin domain'e eklediği her şey —sosyal bağlantılar, avatarlar, metin kayıtları— bu yapının içinde yaşar. Bu alanlar bir şirketin MongoDB kümesinde (cluster) saklanmaz. Bunlar sözleşme durumundaki (contract state) alanlardır; yani kayıt defterini nasıl okuyacağını bilen herhangi bir uygulama aynı profili görüntüleyebilir. Veritabanı senkronizasyonu gerektirmez.
Beni Yavaşlatan İki Tuzak
İnşa sürecindeki her şey iki satır ve bir başarı bayrağından ibaret değildi. Sizin de tekrarlamamanız için açıkça belirtmeye değer iki spesifik engelle karşılaştım.
Ağ Uyuşmazlığı (Network Mismatch)
SDK hem mainnet hem de preprod ortamlarını destekler. "Mevcut olmayan" bir domaini hata ayıklamakla (debug) epey vakit kaybettim. İsim doğruydu. Kod doğru görünüyordu. Sağlayıcı çalışıyordu. Sorun, domainin kendisi mainnet üzerinde kayıtlıyken scriptimin preprod ortamına sorgu atıyor olmasıydı. Hata, eksik bir domain gibi görünüyordu ama aslında eksik bir ağ bağlamıydı (network context).
Eğer bir ismi çözümlüyor ve hata alıyorsanız, başka bir şeyi hata ayıklamadan önce sağlayıcı ayarlarını kontrol edin. networkId değerinizin, domainin gerçekten basıldığı (minted) ağ ile eşleştiğinden emin olun. Bu, geriye dönüp bakıldığında bariz gelen ancak sözleşme mantığının sorun olduğunu varsaydığınızda fark edilmesi gerçekten zor olan bir hata türüdür.
Serileştirme Sancısı (Serialization Pain)
SDK'dan dönen veri standart JavaScript değildir. BigInt değerleri ve Map nesneleri içerir. Bunu bir tarayıcıya göndermek için doğrudan JSON.stringify içine aktarmaya çalışırsanız, hata verecektir veya veriyi sessizce düşürecektir. BigInt'in yerel bir JSON temsili yoktur ve Map, düz nesneler (plain objects) gibi serileştirilmez.
Sonuç olarak özel bir serileştirici (serializer) yazmak zorunda kaldım. Bu serileştirici, yanıt sunucudan ayrılmadan önce sonuç nesnesini tarar, BigInt değerlerini dizgilere (string) dönüştürür ve Map örneklerini normal nesnelere dönüştürür. Eğer Midnight verilerini bir frontend'e sunan bir API oluşturuyorsanız, bu adımı erkenden planlayın. Sırf JavaScript olduğu için SDK çıktısının anında frontend dostu olduğunu varsaymayın.
Mimari
I kept the stack deliberately boring. The backend is a Node server running Express. It imports the @midnames/sdk, runs the resolution logic, handles the serialization gymnastics, and serves clean JSON. The frontend is plain HTML and vanilla JavaScript. No build step. No framework. No wallet adapter.
I chose to run the SDK on the backend rather than the browser for a few practical reasons. It keeps any provider configuration out of the client, it gives me one place to fix the serialization mess, and it means the frontend only needs to fetch data and render it.
Here is the part that surprised me most: resolving a name is a public read operation. You do not need a wallet connection. You do not need a signature. You do not need the user to log in with anything. If the domain exists, the contract state is visible to anyone who asks. That is a meaningful difference from the typical web3 flow where every interaction starts with “connect wallet.” Reading identity on Midnight is permissionless in the same way that reading a public website is permissionless.
The Real Takeaway
Building this viewer reminded me that the hardest part of blockchain development is rarely the blockchain itself. Midnight already solved the difficult problem: letting people own their name and profile without a central database. The hard part, from a builder’s perspective, was remembering which network I was pointing at and writing a helper function to clean up data types.
The protocol gives you portable identity. Your job as a developer is simply to read it correctly and get out of the user’s way. Keep the architecture simple, separate the chain-facing logic from the UI, and treat public reads like what they are: ordinary database queries that just happen to live on a distributed ledger.
If you want to see the code or run it yourself, the full source is available at https://github.com/tomiin/midnames-profile-viewer.
