İçeriğe geç
Muhammet Şafak
en
Diller 3 dk okuma

Go'da HTTP servisi yazmak: standart kütüphane yeter mi

Go'nun net/http paketiyle küçük bir HTTP servisi kurarken neyin geldiğini, neyin gelmediğini ve framework eşiğini tartışıyorum.

Kapak görseli — gri zeminde küçük bir sunucu kutusu: ön yüzündeki mor plakada net/http yazısı, altında iki RJ45 ağ girişi

Go öğrenirken kendime verdiğim kurallardan biri şuydu: bir şeyi standart kütüphane ile kurmayı denemedikçe dışarıdan kütüphane eklemeyeceğim. Bu kural bazen can sıktı, çoğu zaman öğretti — Go öğrenmeye neden başladığımı anlattığım yazıdaki niyetin doğrudan sonucuydu: dili araçlarının arkasından değil, kendisinden tanımak. HTTP servisi bu kuralı gerçekten test eden alan oldu.

PHP’de her şeyin üstünde bir framework çalışıyor; Laravel, Slim, ne olursa. Ham PHP ile HTTP sunucusu yazmak teorik olarak mümkün ama kimse yapmıyor. Go’da ise net/http paketi gerçekten kullanılabilir, production’da çalışan servisler standart kütüphaneyle yazılıyor. Bu fark zihniyet değişikliğine zorluyor.

net/http ile temel sunucu

Bir Go HTTP sunucusunu çalıştırmak birkaç satır:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintln(w, `{"status":"ok"}`)
    })

    http.ListenAndServe(":8080", nil)
}

Çalıştırıyor, port 8080’de yanıt veriyor. Bağımlılık sıfır. Bu basitlik ilk başta tatmin edici; ama bir API yazmaya başladığınızda sınırlar ortaya çıkıyor.

Standart kütüphanenin eksikleri

net/http’nin HandleFunc ile kayıt mekanizması yeterince esnek değil. Route parametresi — /users/123 gibi dinamik segmentler — bu yazıyı yazdığım sırada standart kütüphanede yoktu. Her yolu elle parse etmek gerekiyordu:

http.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) {
    // "/users/" sonrasını elle çekiyoruz
    id := r.URL.Path[len("/users/"):]
    if id == "" {
        // liste döndür
        return
    }
    // tekil kayıt döndür
})

Bu yaklaşım iki-üç rota için idare ediyor, on rota için çekilmez hale geliyor. HTTP metodu (GET/POST/PUT/DELETE) ayrımını da elle yapmak gerekiyor:

switch r.Method {
case http.MethodGet:
    // GET işle
case http.MethodPost:
    // POST işle
default:
    http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}

Bu switch bloğunu her handler’a kopyalamak kodu hızla şişiriyor. Standart kütüphane bu ortak pattern’ı soyutlamıyor; bunu kendiniz yazmak ya da bir kütüphaneye devretmek gerekiyor.

Buraya bugünden bir not düşmek gerekiyor: bu eksik kapandı. Go 1.22, net/http’nin ServeMux’ına gelişmiş desen eşleştirmesi ekledi/items/{id} gibi joker segmentler artık standart kütüphanede tanınıyor ve segmentin değerine Request.PathValue ile ulaşılıyor; "POST /items/create" biçiminde kayıt yapıldığında handler yalnız o metotla çağrılıyor. Yani yukarıdaki elle parse ve switch r.Method kalıpları 2024’ten beri zorunlu değil. Yazının geri kalanındaki muhakeme — bir kütüphaneyi ne zaman almalı — duruyor; değişen, eşiğin yeri.

Framework eşiği nerede?

Bir kütüphane almadan önce şu soruyu soruyorum: standart kütüphanenin sağlamadığı şey ne kadar jenerik bir ihtiyaç? Route parametresi ve HTTP metodu yönlendirmesi jenerik ve evrensel; her serviste gerekli.

Bu noktada üçüncü parti kütüphanelere bakıyorum. Go ekosisteminde gorilla/mux ve chi bu boşluğu dolduran hafif router’lar. Framework değiller; yalnızca net/http’nin üstüne route parametresi ve metot eşleştirme ekliyorlar, standart http.Handler interface’ine uyuyorlar.

import "github.com/go-chi/chi"

r := chi.NewRouter()

r.Get("/users/{id}", func(w http.ResponseWriter, req *http.Request) {
    id := chi.URLParam(req, "id")
    // id ile işle
})

http.ListenAndServe(":8080", r)

Temel felsefe korunuyor: standart kütüphanenin interface’lerine uyan, başka kütüphanelerle çakışmayan bir eklenti. Laravel benzeri büyük bir bağımlılık yığını değil.

chi’nin beni ikna eden tarafı standart http.Handler interface’ine tam uyum — kendi tanıtımında “net/http ile %100 uyumlu” ve “dış bağımlılık yok, yalnız Go standart kütüphanesi” diyor. Bir kütüphane seçerken bu uyuma dikkat ediyorum; standart interface’i bozan bir router, ekosisteme uymayan middleware ya da yardımcılarla çalışmayı zorlaştırıyor. chi ile yazdığım handler’ı başka herhangi bir net/http uyumlu yapıyla kullanmak mümkün.

JSON yanıtları ve hata yönetimi

Standart kütüphanede JSON serialization için encoding/json paketi var ve iyi çalışıyor:

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func writeJSON(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

Bu küçük helper fonksiyon tekrar yazmayı ortadan kaldırıyor; tüm handler’lar bunu kullanıyor.

PHP ile karşılaştırma

Go’da HTTP servisi yazmak PHP’de framework olmadan yazmaktan farklı: standart kütüphane gerçekten production’a hazır, bakımlı ve hızlı. Ancak Laravel’in sunduğu kimlik doğrulama, ORM, queue, e-posta gibi katmanlar Go’da yok — ve olması gerekmiyor. Go servisleri çoğunlukla daha dar bir sorumluluğu çok iyi yapıyor; geniş kapsam için başka diller devreye giriyor.


Sonuç olarak: Go’da standart kütüphane çok fazlasını karşılıyor, ama her şeyi değil. Route parametresi ve metot yönlendirmesi için hafif bir kütüphane makul bir ödünleşim. Tam framework’e geçmek çoğu durum için erken bir karar — ihtiyaç gerçekten büyüdüğünde alınabilir bir karar.


Kaynaklar

Bu yazının arkasındaki araştırmalar

PHP ile Go'yu aynı OAuth2 API'de ölçtüm: 10.000 yazmada fark yok, 50.000 okumada var

Her istekte OAuth2 token'ı doğrulayıp PostgreSQL'e yazan ya da okuyan aynı API, dört çekirdekte PHP-FPM, FrankenPHP worker ve Go ile saniyede 10.000 yazmayı ve 50.000 okumayı kaç CPU'yla karşılıyor?

Bulgu

Saniyede 10.000 yazmada üç aday da hedefi beş tekrarın beşinde tutturdu, p99 üçünde de 2,5 ms'yi aşmadı: bu yükte dil bir kapasite kalemi değil. Saniyede 50.000 okumayı dört çekirdekte yalnız Go tutturdu (p99 6,45 ms); PHP-FPM 23.528'de, FrankenPHP 22.859'da kaldı. İstek başına CPU okumada Go'da 64, FrankenPHP'de 117, PHP-FPM'de 168 mikrosaniye. 50.000 okuma için instance hesabı Go'ya 3,4, iki PHP adayına 8,2–8,8 çekirdek yazdırıyor. FrankenPHP'nin CPU tasarrufu kapasiteye dönmüyor: doygunken dört çekirdeğin birini boşta bırakıyor.

bugün ölçüldü

Orta güven

Bu konuda sorulanlar

Bu konudaki deneyler

Dile özgü serileştirme yerine dondurulmuş bir JSON zarfı; polyglot kuyruk standardı BabelQueue'ya dönüştü.

Şu an ne yapıyor

Dondurulmuş JSON zarfı dört dilde — PHP, Python, Go, Node.js — aynı baytları okutuyor; sidecar ya da broker eklentisi gerekmiyor. Polyglot kuyruk kuran ekipler spesifikasyonu ve SDK'ları bugün kullanabilir.

Açık kaynak JSON Redis RabbitMQ +4 daha
Mart 2026 — Haziran 2026

Diff'i makineden çıkarmadan bir modele denetleten, sağlayıcıdan bağımsız Go CLI; CommitBrief ürününe dönüştü.

Şu an ne yapıyor

Diff hiç makineden çıkmadan yerel kod incelemesi yapıyor; `Provider` arayüzü sayesinde Anthropic, OpenAI, Gemini ve Ollama aynı sözleşmeyle takılıyor. İmzalı binary'ler yayında — diff'ini bir bulut sağlayıcısına vermek istemeyen herkes kurabilir.

Açık kaynak Go Cobra go-git +4 daha
Ocak 2026 — Nisan 2026
Etiketler: #Go
Uzmanlık: Go Geliştirici
Paylaş:

Son güncelleme:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi