Skip to main content

Port Karmaşasına Son: Portless ile Yerel Geliştirme

Salı sabahı, frontend’ini başlatıyorsun ve terminal şunu söylüyor: "Port 3000 is already in use". Tabii ya. Bir saat önce sadece bir dakikalığına bakmak istediğin diğer proje hâlâ arka planda çalışıyor. Sen de 3001’e geçiyorsun. Kısa süre sonra API de 3000’i istiyor, admin paneli 5173’e oturuyor ve bir yerlerde bir Storybook 6006’da çalışıyor.

Birkaç hafta birden fazla projeyle çalıştıktan sonra aslında saçma olan bir soru soruyorsun kendine: Hangi numara hangi uygulamaya aitti? Tarayıcı geçmişin sadece localhost kayıtlarından oluşuyor, yer imlerin işe yaramıyor ve ekipteki README’de "3000 portunda çalışır, doluysa değil" gibi bir cümle duruyor.

Tam bu noktada Portless devreye giriyor. Port numaraları yerine her projeye sabit, anlamlı bir isim veriliyor, örneğin https://myapp.localhost. Bu yazıda nasıl çalıştığına, günlük işine ne kazandırdığına ve sınırlarının nerede olduğuna bakıyoruz.

Portlar neden sandığından daha çok yoruyor?

Sorun sıradan, ama her gün küçük parçalar halinde dikkatini yiyor. Her framework bir varsayılan portla geliyor: Next.js ve Nuxt 3000’de, Vite 5173’te, Angular 4200’de, Astro 4321’de, Storybook 6006’da başlıyor. Aynı stack’le iki projeyi paralel çalıştıran biri kaçınılmaz olarak çakışmalara düşüyor.

Sonuçlar hata mesajının ötesine geçiyor:

  • Değişen numaralar: Bir proje bugün 3000’de, yarın 3002’de çalışıyor, çünkü dün bir şey açık kalmıştı. Numarayı bilen her şey bozuluyor.
  • Koda gömülü yapılandırma: OAuth yönlendirme URI’leri, CORS ayarları, webhook hedefleri ve .env dosyaları çoğu zaman sabit port içeriyor. Port değişince yapılandırma da değişmek zorunda.
  • Paylaşılan cookie’ler: localhost üzerindeki tüm uygulamalar, hangi portta çalışırlarsa çalışsınlar aynı cookie alanını paylaşıyor. Bir projedeki giriş, diğerindeki oturumun üzerine yazabiliyor. Bu tür hataları bulmak zor.
  • Zihinsel yük: Hiçbir bilgi değeri taşımayan eşleştirmeleri aklında tutuyorsun. "5173 dashboard’dur" kimsenin ihtiyaç duymadığı bir bilgi.

Buna yeni bir etken ekleniyor: Dev server’ları yerelde başlatıp tarayıcıda test eden yapay zekâ ajanları. Onların da bir uygulamanın şu anda hangi adreste çalıştığını bilmesi gerekiyor. Sabit bir isim, hem insanlar hem de ajanlar için değişebilen bir sayıdan daha kolay.

Portless’ın arkasındaki fikir

Portless sorunu, portları görüş alanından çıkararak çözüyor. Arka planda 443 portunu dinleyen yerel bir reverse proxy çalışıyor. Her uygulama orada bir host adı kaydediyor ve proxy istekleri ilgili porta yönlendiriyor.

Tarayıcı (myapp.localhost) -> Proxy (Port 443) -> Uygulama (rastgele port)

Gerçek portu Portless kendisi seçiyor, 4000 ile 4999 aralığından, ve uygulamaya PORT ortam değişkeniyle iletiyor. Onu bilmen de yazman da gerekmiyor. İsim sabit kalıyor, arkasındaki numara değişebiliyor.

Bunun sistem DNS’ine dokunmadan çalışması .localhost uzantısı sayesinde. Bu uzantıyla biten isimler güncel sistemlerde otomatik olarak 127.0.0.1 adresine çözülüyor. Yani ne hosts dosyasına kayıt ne de yerel bir DNS sunucusu gerekiyor.

Baştan bir güvenlik notu: Proxy varsayılan olarak yalnızca 127.0.0.1 ve ::1 loopback adreslerini dinliyor. LAN modunu açıkça etkinleştirmedikçe ağdan erişilemiyor.

Kurulum ve ilk başlatma

Giriş eşiği düşük. Node.js 24 veya daha yeni bir sürüm gerekiyor, macOS, Linux ve Windows destekleniyor. Kurulum npm ile, en iyisi global olarak yapılıyor:

npm install -g portless

Alternatif olarak proje başına dev dependency olarak da eklenebilir. Ancak o zaman ekip üyeleri farklı sürümler kullanabilir ve bu, 1.0 öncesi bir araç için pek zararsız değil (bununla ilgili ayrıntılar tuzaklar bölümünde).

Sonrasında proje klasöründe tek bir komut yeterli:

portless

Portless, package.json dosyandaki dev script’ini okuyor ve proxy üzerinden başlatıyor. İsmi package.json’dan, Git kökünden veya klasör adından türetiyor. Farklı bir isim istersen bir portless.json oluşturuyorsun:

{ "name": "myapp" }

Açık açık yazmayı tercih edersen isim ve komutu doğrudan veriyorsun:

portless myapp next dev
# -> https://myapp.localhost

Bunu package.json içine kalıcı olarak da yazabilirsin, böylece ekipte herkes aynı komutu kullanır: "dev": "portless myapp next dev".

İlk başlatmada ne oluyor? Portless yerel bir sertifika otoritesi (CA) oluşturuyor, onu güvenilir olarak tanımlıyor ve 443 portunu bağlıyor. Bunun için macOS ve Linux’ta yönetici hakları istiyor. Bu bir kez oluyor ve yerelde HTTPS için kaçınılmaz.

Peki frameworkler? Next.js, Express ya da Nuxt zaten PORT değişkenine uyuyor. Onu yok sayan Vite, Astro, Angular, Expo ve diğerleri için Portless uygun --port bayrağını (gerekirse --host bayrağını da) otomatik ekliyor. Script’ine dokunman gerekmiyor.

Birden fazla servis için alt alan adları

Bir proje nadiren tek bir uygulamadan oluşuyor. Genelde bir frontend, bir API, belki bir dokümantasyon sitesi ya da admin paneli var. Portless ile her servis proje adının altında bir isim alıyor:

portless api.myapp pnpm start
# -> https://api.myapp.localhost

portless docs.myapp next dev
# -> https://docs.myapp.localhost

Bu kozmetikten fazlası. Yapı, projenin prodüksiyonda nasıl kurulduğunu yansıtıyor. API’ye api.myapp.localhost üzerinden erişen bir frontend, localhost:4000 çağrısına göre sonraki api.senin-alan-adin.com adresine çok daha benziyor. Böylece cookie domain’lerini, CORS kurallarını ve yönlendirme URI’lerini çok daha gerçekçi test edebiliyorsun.

Bir de yan etki: Yukarıdaki cookie sorunları ortadan kalkıyor. Her host adının kendi cookie alanı var, uygulamalar artık birbirine karışmıyor.

Çakışmasız Git worktree’ler

Git worktree’lerle çalışıyorsan, örneğin bir bugfix ve bir feature üzerinde paralel çalışmak için, sorunu daha da keskin biçimde tanırsın: İki checkout da aynı kodu içeriyor ve aynı portu istiyor.

Portless, portless run kullandığında worktree’leri otomatik tanıyor. Ana dizinde isim değişmiyor, bağlı bir worktree’de branch adı alt alan adı olarak başa ekleniyor:

# Ana worktree
portless run next dev   # -> https://myapp.localhost

# "fix-ui" branch’indeki worktree
portless run next dev   # -> https://fix-ui.myapp.localhost

portless run komutunu package.json içine bir kez yazıyorsun ve her yerde çalışıyor. Branch başına yapılandırma yok, --force yok, çakışma yok. Gerekirse --name ile temel adı değiştirebilirsin, worktree öneki korunuyor.

Özellikle kendi kodunun yanında yapay zekâ ajanlarını ayrı worktree’lerde çalıştırıyorsan bu çok pratik. Her örneğin kendi, tahmin edilebilir adresi oluyor.

Yerelde HTTPS ve kendi alan adı uzantın

Portless’ta HTTPS varsayılan olarak açık, HTTP/2 dahil. Bu, kulağa geldiği kadar kendiliğinden değil. Birçok tarayıcı özelliği (service worker’lar, bazı API’ler, secure cookie’ler) HTTP üzerinde prodüksiyondakinden farklı davranıyor. Yerelde baştan HTTPS ile çalışan, bu farkları daha erken fark ediyor. Yine de düz HTTP gerekirse --no-tls ile geçebilirsin.

Portless varsayılan olarak .localhost kullanıyor. Ama uzantıyı değiştirebilirsin:

portless proxy start --tld test
portless myapp next dev
# -> https://myapp.test

Proxy bunun için hosts dosyasını kendisi güncelliyor, çünkü .test, .localhost’un aksine otomatik çözülmüyor. --tld bayrağını tekrarlayarak aynı anda birden fazla uzantı da kullanabilirsin.

Özellikle ilginç olan: "TLD" olarak sahibi olduğun bir alan adını da kullanabilirsin, örneğin dev.example.com. O zaman uygulaman yerelde https://myapp.dev.example.com adını alıyor. URL’ler prodüksiyonla aynı yapıya sahip oluyor ve OAuth yönlendirmeleri, alt alan adları arası cookie’ler ve host tabanlı yönlendirme her iki ortamda da aynı davranıyor.

Uzantı seçimi için net bir öneri var: .test, IANA tarafından ayrılmıştır ve bu yüzden gerçek alan adlarıyla asla çakışmaz. .local (mDNS/Bonjour ile çakışmalar) ve .dev (Google’a ait ve HSTS ile HTTPS’i zorunlu kılar) uzantılarından kaçınmalısın.

Tuzaklar ve ödünleşimler

Hiçbir araç kusursuz değil. Şu noktaları önceden bilmelisin:

  • Yönetici hakları: 443 portu ve yerel CA yükseltilmiş yetki gerektiriyor. Kurumsal yönetilen bilgisayarlarda bu, önce BT ile çözülmesi gereken bir engel olabilir.
  • 1.0 öncesi: Portless henüz 1.0 sürümünde değil. İç durum klasörünün biçimi sürümler arasında değişebilir, bu durumda portless trust komutunu yeniden çalıştırman gerekebilir. Global kurulum, ekipte farklı sürümleri önlüyor.
  • Node.js 24 veya üzeri: Hâlâ eski bir Node sürümündeysen önce güncellemen gerekiyor.
  • Karmaşık script’ler: Birleşik komutlarda (&&, pipe’lar, NODE_ENV=production vite) ya da başka script’lere yönlendirmelerde Portless port bayrağı eklemiyor. Orada portu kendin ayarlıyorsun.
  • Süreç sınırları: Reverse proxy ek bir katman. Bir şey ters giderse bakacak bir yer daha oluyor.

Bunların hiçbiri engelleyici değil. Ama aracı hemen tüm ekipte devreye almak yerine önce tek bir projede denemekte fayda var.

Sonuç: Numaralar yerine isimler

Portless, çoğumuzun artık fark etmeyecek kadar uzun süredir taşıdığı bir sorunu çözüyor. Uygulama başına sabit bir isim, uğraştırmayan HTTPS, düzgün alt alan adları ve sorunsuz çalışan worktree desteği: Bunlar günlük geliştirmedeki küçük sürtünmeleri gözle görülür biçimde azaltıyor.

Denemenin maliyeti düşük. Global olarak kuruyorsun, bir projede "dev": "next dev" yerine portless yazıyorsun ve ilk başlatmadan sonra yeni çalışma şeklini sevip sevmediğini görüyorsun. Düzenli olarak birden fazla projeyi paralel çalıştırıyorsan, worktree’ler ya da ajanlarla çalışıyorsan veya yerelde prodüksiyona mümkün olduğunca yakın test etmek istiyorsan denemeye değer.

Sıradaki adımın: Bir proje seç, Portless’ı kur ve istediğin bir isimle başlat. Başka bir şeye gerek yok.

Yerel geliştirme ortamını ve ekip iş akışlarını düzene sokmak mı istiyorsun, yoksa bir web projesinde desteğe mi ihtiyacın var? Daha fazlasını Web Geliştirme sayfasında bulabilir ya da bana doğrudan yazabilirsin.