Neler yeni

Yazılım Forum

Tüm özelliklerimize erişmek için şimdi bize katılın. Kayıt olduktan ve giriş yaptıktan sonra konu oluşturabilecek, mevcut konulara yanıt gönderebilecek, itibar kazanabilecek, özel mesajlaşmaya erişebilecek ve çok daha fazlasını yapabileceksiniz! Bu hizmetlerimiz ise tamamen ücretsiz ve kurallara uyulduğu sürece sınırsızdır, o zaman ne bekliyorsunuz? Hadi, sizde aramıza katılın!

Go ile Robust API'ler Geliştirmek: Kapsamlı Bir Rehber

Go ile Robust API'ler Geliştirmek: Kapsamlı Bir Rehber

Günümüz mikroservis mimarilerinde ve modern web uygulamalarında, API'ler (Uygulama Programlama Arayüzleri) vazgeçilmez bir role sahiptir. Performans, eşzamanlılık ve ölçeklenebilirlik gibi ihtiyaçlar arttıkça, Go (Golang) dili bu alanda popüler bir tercih haline gelmiştir. Go'nun sade sözdizimi, güçlü standart kütüphanesi ve eşzamanlılık ilkelikleri, yüksek performanslı ve bakımı kolay API'ler geliştirmek için ideal bir ortam sunar. Bu rehberde, Go ile sıfırdan bir API geliştirmeyi, temel kavramlardan ileri seviye konulara kadar adım adım inceleyeceğiz.

Neden Go?
  • Performans: Derlenmiş bir dil olarak Go, olağanüstü performans sunar. Özellikle I/O yoğun uygulamalarda bu avantaj belirginleşir.
  • Eşzamanlılık: Goroutine'ler ve Channel'lar sayesinde eşzamanlı programlama çok daha kolay ve verimli hale gelir. Bu, eş zamanlı istekleri işleyen API'ler için kritik öneme sahiptir.
  • Basitlik ve Okunabilirlik: Go'nun minimalist tasarımı ve güçlü kod biçimlendirme araçları (go fmt), takım içinde tutarlı ve okunabilir kod yazmayı teşvik eder.
  • Geniş Standart Kütüphane: HTTP sunucuları, JSON işleme, veritabanı sürücüleri gibi birçok temel ihtiyacı karşılayan zengin bir standart kütüphaneye sahiptir. Bu, harici bağımlılıkları azaltır.
  • Hızlı Derleme Süresi: Büyük projelerde bile derleme süreleri oldukça kısadır, bu da geliştirme sürecini hızlandırır.

Ortam Kurulumu
Go ile API geliştirmeye başlamadan önce sisteminizde Go kurulu olmalıdır. Resmi Go web sitesinden (https://go.dev/doc/install) işletim sisteminize uygun kurulum yönergelerini takip edebilirsiniz. Kurulumdan sonra terminalinizde aşağıdaki komutları çalıştırarak Go'nun doğru bir şekilde kurulduğundan emin olun:
Kod:
go version
go env GOPATH
`GOPATH` genellikle kullanıcı dizininizin altında `go` adında bir klasör olacaktır. Modül tabanlı geliştirme sayesinde `GOPATH`'in önemi azalmış olsa da, Go ortamının temelini anlamak önemlidir.

Temel HTTP Sunucusu Oluşturma
Go'nun `net/http` paketi, çok basit bir HTTP sunucusu oluşturmak için ihtiyacınız olan her şeyi sağlar. Aşağıda "Merhaba API!" yanıtı veren temel bir sunucu örneği bulunmaktadır:
Kod:
package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Merhaba API! Bu ilk Go API'niz.")
	})

	log.Println("Sunucu 8080 portunda dinleniyor...")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Bu kodu `main.go` olarak kaydedip `go run main.go` ile çalıştırdığınızda, tarayıcınızdan `http://localhost:8080` adresine giderek "Merhaba API! Bu ilk Go API'niz." mesajını görebilirsiniz.

Yönlendirme (Routing)
Yukarıdaki örnek tek bir rota için yeterli olsa da, gerçek dünya API'lerinde birden fazla rota ve farklı HTTP metotları (GET, POST, PUT, DELETE) gerekir. Go'nun `net/http` paketi temel yönlendirme sağlar, ancak karmaşık API'ler için üçüncü taraf bir router kütüphanesi kullanmak daha yaygındır. `Gorilla Mux` veya `Chi` gibi kütüphaneler popülerdir. Biz burada `Chi`'yi kullanarak bir örnek vereceğiz:
Kod:
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

func main() {
	r := chi.NewRouter()

	// Bazı middleware'ler ekleyelim
	r.Use(middleware.Logger) // Her isteği loglar
	r.Use(middleware.Recoverer) // Panik durumlarını yakalar

	// Rotaları tanımlayalım
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Ana sayfa"))
	})

	r.Get("/urunler", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Tüm ürünler listesi"))
	})

	r.Get("/urunler/{id}", func(w http.ResponseWriter, r *http.Request) {
		productID := chi.URLParam(r, "id")
		fmt.Fprintf(w, "Ürün ID: %s", productID)
	})

	r.Post("/urunler", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Yeni ürün oluşturuldu"))
	})

	log.Println("Sunucu 8080 portunda Chi ile dinleniyor...")
	log.Fatal(http.ListenAndServe(":8080", r))
}
Bu kodu çalıştırmak için öncelikle `go get github.com/go-chi/chi/v5` ve `go get github.com/go-chi/chi/v5/middleware` komutlarını çalıştırmanız gerekebilir. `Chi` ile parametreleri (örn. `{id}`) kolayca yakalayabilir ve farklı HTTP metotları için ayrı handler'lar tanımlayabilirsiniz.

JSON İstek ve Yanıtlarını İşleme
API'lerin temel işlevlerinden biri JSON verileri alıp göndermektir. Go'nun `encoding/json` paketi bu konuda oldukça güçlüdür.

JSON İsteklerini Ayrıştırma (Decoding):
İstemciden gelen JSON verilerini Go struct'larına dönüştürmek için `json.NewDecoder` kullanırız.
Kod:
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv" // strconv kütüphanesini import edin

	"github.com/go-chi/chi/v5"
)

type Product struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Price float64 `json:"price"`
}

var products = []Product{
	{ID: 1, Name: "Klavye", Price: 150.0},
	{ID: 2, Name: "Fare", Price: 75.0},
}

func main() {
	r := chi.NewRouter()

	r.Get("/urunler", getProducts)
	r.Get("/urunler/{id}", getProductByID)
	r.Post("/urunler", createProduct)

	log.Println("JSON API sunucu 8080 portunda dinleniyor...")
	log.Fatal(http.ListenAndServe(":8080", r))
}

func getProducts(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(products)
}

func getProductByID(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	idStr := chi.URLParam(r, "id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		http.Error(w, "Geçersiz ürün ID'si", http.StatusBadRequest)
		return
	}

	for _, p := range products {
		if p.ID == id {
			json.NewEncoder(w).Encode(p)
			return
		}
	}

	http.Error(w, "Ürün bulunamadı", http.StatusNotFound)
}

func createProduct(w http.ResponseWriter, r *http.Request) {
	var newProduct Product
	err := json.NewDecoder(r.Body).Decode(&newProduct)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Yeni ID atama (basit bir örnek için)
	if len(products) > 0 {
		newProduct.ID = products[len(products)-1].ID + 1
	} else {
		newProduct.ID = 1
	}
	products = append(products, newProduct)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(newProduct)
}
`json:"id"` gibi etiketler (struct tag), Go struct alanlarının JSON anahtarlarıyla nasıl eşleştirileceğini belirtir. `json.NewDecoder(r.Body).Decode(&newProduct)` ile gelen JSON istek gövdesini `Product` struct'ına çözümleriz.

JSON Yanıtları Oluşturma (Encoding):
Go struct'larını JSON yanıtına dönüştürmek için `json.NewEncoder` kullanırız.
`json.NewEncoder(w).Encode(products)` komutu, `products` slice'ını doğrudan `http.ResponseWriter`'a JSON olarak yazar. `w.Header().Set("Content-Type", "application/json")` ile yanıtın tipini belirtmek önemlidir.

Veritabanı Entegrasyonu (PostgreSQL Örneği)
Çoğu API, verileri kalıcı olarak depolamak için bir veritabanına ihtiyaç duyar. Go'nun `database/sql` paketi, veritabanı işlemlerini gerçekleştirmek için jenerik bir arayüz sağlar. Belirli bir veritabanı için ise ilgili sürücüyü kullanmanız gerekir (örn. PostgreSQL için `github.com/lib/pq`).

Veritabanı bağlantısı açmak ve kapatmak performansı etkileyebileceğinden, genellikle bir bağlantı havuzu (connection pool) kullanmak en iyi yaklaşımdır. `database/sql` paketi zaten dahili bir bağlantı havuzu yönetimine sahiptir.

Basit bir PostgreSQL bağlantı ve sorgulama örneği:
Kod:
package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/lib/pq" // PostgreSQL sürücüsü
)

func main() {
	connStr := "user=postgres password=root dbname=yourdb sslmode=disable"
	db, err := sql.Open("postgres", connStr)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close() // Fonksiyon sonunda bağlantıyı kapat

	err = db.Ping() // Bağlantıyı test et
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Veritabanına başarıyla bağlanıldı!")

	// Basit bir sorgu örneği
	var version string
	err = db.QueryRow("SELECT version()").Scan(&version)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("PostgreSQL Sürümü:", version)

	// Tablo oluşturma örneği (bir kez çalıştırın)
	// createTableSQL := `
	// CREATE TABLE IF NOT EXISTS users (
	// id SERIAL PRIMARY KEY,
	// name TEXT NOT NULL,
	// email TEXT UNIQUE NOT NULL
	// );`
	// _, err = db.Exec(createTableSQL)
	// if err != nil {
	// log.Fatal(err)
	// }
	// fmt.Println("Users tablosu oluşturuldu veya zaten mevcut.")

	// Veri ekleme
	// insertSQL := `INSERT INTO users(name, email) VALUES($1, $2) RETURNING id`
	// var userID int
	// err = db.QueryRow(insertSQL, "Alican", "alican@example.com").Scan(&userID)
	// if err != nil {
	// log.Fatal(err)
	// }
	// fmt.Println("Yeni kullanıcı eklendi, ID:", userID)

	// Veri okuma
	rows, err := db.Query("SELECT id, name, email FROM users")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	fmt.Println("\nKullanıcılar:")
	for rows.Next() {
		var id int
		var name, email string
		if err := rows.Scan(&id, &name, &email); err != nil {
			log.Fatal(err)
		}
		fmt.Printf("ID: %d, İsim: %s, Email: %s\n", id, name, email)
	}
	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}
}
Not: Bu kodun çalışması için bir PostgreSQL veritabanı sunucusunun kurulu ve erişilebilir olması gerekmektedir. `connStr` değerini kendi veritabanı bilgilerinizle değiştirmeniz gerekmektedir. `_ "github.com/lib/pq"` satırı, sürücüyü sadece paketin `init` fonksiyonunun çalışması için içe aktarır, doğrudan paketi kullanmayız.

Hata Yönetimi
API'lerde hataları doğru şekilde yönetmek, hem istemci tarafının anlaşılabilir yanıtlar almasını sağlar hem de sunucu tarafında sorun gidermeyi kolaylaştırır. Go'da hatalar genellikle `error` arayüzü ile temsil edilir.

API'lerde hata yönetimi için yaygın yaklaşımlar:
  • HTTP Durum Kodları: Standart HTTP durum kodları (200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error) kullanarak hatanın doğasını belirtin.
  • JSON Hata Yanıtları: Hata detaylarını içeren JSON formatında yanıtlar döndürün. Örneğin: `{"error": "Ürün bulunamadı", "code": "NOT_FOUND"}`
  • Hata Kaydetme (Logging): Sunucu tarafındaki hataları loglayarak izlenebilirlik sağlayın. `log` paketi veya daha gelişmiş loglama kütüphaneleri (örn. `logrus`) kullanılabilir.

Örnek bir hata yanıtı oluşturucu fonksiyon:
Kod:
func respondWithError(w http.ResponseWriter, code int, message string) {
	respondWithJSON(w, code, map[string]string{"error": message})
}

func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
	response, err := json.Marshal(payload)
	if err != nil {
		log.Printf("JSON'a dönüştürme hatası: %v", err)
		http.Error(w, "Sunucu iç hatası", http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	w.Write(response)
}

// Kullanım örneği (daha önceki getProductByID fonksiyonundan bir kesit):
// if err != nil {
//     respondWithError(w, http.StatusBadRequest, "Geçersiz ürün ID'si")
//     return
// }
// ...
// respondWithError(w, http.StatusNotFound, "Ürün bulunamadı")

Ara Katman Yazılımları (Middleware)
Middleware, HTTP isteği bir handler'a ulaşmadan önce veya ulaştıktan sonra belirli işlemlerin yapılmasını sağlayan fonksiyonlardır. Loglama, kimlik doğrulama, yetkilendirme, CORS yönetimi gibi işlevler için kullanılırlar. Chi router'ı zaten bazı yerleşik middleware'ler sunar (`middleware.Logger`, `middleware.Recoverer`). Kendi middleware'inizi yazmak da oldukça basittir:
Kod:
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := r.Header.Get("Authorization")
		if token == "" || token != "Bearer secret_token" { // Basit bir kontrol
			http.Error(w, "Yetkilendirme gerekli", http.StatusUnauthorized)
			return
		}
		// İsteğe ek bilgi ekleyebilirsiniz (örn. kullanıcı ID'si)
		// ctx := context.WithValue(r.Context(), "userID", 123)
		// next.ServeHTTP(w, r.WithContext(ctx))
		next.ServeHTTP(w, r)
	})
}

// Kullanım örneği:
// r.With(AuthMiddleware).Get("/admin", adminHandler)
`AuthMiddleware` fonksiyonu, gelen isteklerde `Authorization` başlığını kontrol eden basit bir örnek sunar.

Test Etme
Go, yerleşik bir test çatısına sahiptir. API endpoint'lerini test etmek için `net/http/httptest` paketini kullanabilirsiniz. Bu paket, gerçek bir HTTP sunucusu başlatmadan request'leri ve response'ları simüle etmenize olanak tanır.
Kod:
package main_test // main paketini test etmek için main_test

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/go-chi/chi/v5"
	"your_module_path/main" // main paketini import edin
)

// main.go'daki router'ı döndüren bir fonksiyonunuz olduğunu varsayalım
// func setupRouter() *chi.Mux {
//     r := chi.NewRouter()
//     r.Get("/urunler", getProducts)
//     r.Post("/urunler", createProduct)
//     return r
// }

func TestGetProducts(t *testing.T) {
	// router'ı oluşturalım (main.go'daki router kurulumuna benzer)
	// Burası için main.go'daki func main() içindeki router setup'ı kopyalayıp
	// bir fonksiyona taşımak daha iyi olurdu (e.g. `main.SetupRouter()`)
	r := chi.NewRouter()
	r.Get("/urunler", main.GetProducts) // main.GetProducts'ı doğrudan çağırın
	// main.go dosyasında GetProducts fonksiyonunu dışarıdan erişilebilir (büyük harfle başlamalı) yapın

	req, _ := http.NewRequest("GET", "/urunler", nil)
	rr := httptest.NewRecorder()
	r.ServeHTTP(rr, req)

	if status := rr.Code; status != http.StatusOK {
		t.Errorf("Beklenmeyen durum kodu: got %v want %v",
			status, http.StatusOK)
	}

	expected := `[{"id":1,"name":"Klavye","price":150},{"id":2,"name":"Fare","price":75}]`
	// Burada doğrudan string kontrolü yerine JSON ayrıştırma ve karşılaştırma yapmak daha sağlamdır.
	if strings.TrimSpace(rr.Body.String()) != expected { // rr.Body.String()'de boşluklar olabilir
		t.Errorf("Beklenmeyen yanıt gövdesi: got %v want %v",
			rr.Body.String(), expected)
	}
}

func TestCreateProduct(t *testing.T) {
	r := chi.NewRouter()
	r.Post("/urunler", main.CreateProduct) // main.CreateProduct'ı çağırın

	var jsonStr = []byte(`{"name":"Monitör","price":500}`)
	req, _ := http.NewRequest("POST", "/urunler", strings.NewReader(string(jsonStr)))
	req.Header.Set("Content-Type", "application/json")

	rr := httptest.NewRecorder()
	r.ServeHTTP(rr, req)

	if status := rr.Code; status != http.StatusCreated {
		t.Errorf("Beklenmeyen durum kodu: got %v want %v",
			status, http.StatusCreated)
	}

	var product main.Product // main.Product struct'ına erişmek için
	err := json.Unmarshal(rr.Body.Bytes(), &product)
	if err != nil {
		t.Fatalf("Yanıt gövdesi JSON ayrıştırılamadı: %v", err)
	}

	if product.Name != "Monitör" || product.Price != 500 {
		t.Errorf("Ürün verileri eşleşmiyor: got %+v want Name: Monitör, Price: 500", product)
	}
	if product.ID == 0 { // ID'nin otomatik atanıp atanmadığını kontrol et
		t.Errorf("Yeni ürüne ID atanmadı")
	}
}
Not: Test kodunun çalışması için `main.go` dosyasındaki `GetProducts`, `CreateProduct` ve `Product` struct'ının ilk harflerinin büyük olması yani dışarıdan erişilebilir (exported) olması gerekmektedir. Ayrıca `your_module_path` kısmını kendi Go modül adınızla değiştirmeniz gerekmektedir (örn. `github.com/youruser/yourproject`).

Dağıtım (Deployment)
Go uygulamaları derlenmiş ikili dosyalar (binary) olduğundan dağıtımı oldukça kolaydır. Derlenmiş bir Go API uygulamasını herhangi bir Linux, Windows veya macOS sunucusuna kopyalayıp çalıştırabilirsiniz.
  • Derleme: `go build -o myapi .` komutu ile uygulamanızı derleyin. Farklı işletim sistemleri ve mimariler için cross-compilation yapabilirsiniz (örn. `GOOS=linux GOARCH=amd64 go build -o myapi .`).
  • Süreç Yöneticisi: Uygulamanın sunucu yeniden başlatıldığında veya çöktüğünde otomatik olarak tekrar başlaması için `systemd`, `Supervisor` veya `Docker` gibi bir süreç yöneticisi kullanın.
  • Kapsayıcılaştırma (Containerization): Docker gibi kapsayıcı teknolojileri, uygulamanızın bağımlılıklarıyla birlikte izole bir ortamda çalışmasını sağlar ve dağıtımı standartlaştırır.
  • Ters Proxy (Reverse Proxy): Nginx veya Caddy gibi bir ters proxy kullanarak SSL sonlandırma, yük dengeleme ve statik dosya sunumu gibi işlevleri ekleyin. Bu, güvenlik ve performansı artırır.

Sonuç
Bu kapsamlı rehberde, Go diliyle güçlü ve ölçeklenebilir API'ler geliştirmenin temellerini ve bazı ileri düzey konuları inceledik. Go'nun performans, eşzamanlılık ve sadelik avantajları, onu modern API geliştirme için mükemmel bir seçim haline getirir. Standart kütüphane ve zengin üçüncü taraf ekosistemi, geliştiricilere hızlı ve verimli bir şekilde API'ler oluşturma imkanı sunar.

Unutmayın ki gerçek dünya uygulamaları için güvenlik (kimlik doğrulama, yetkilendirme, CORS, input doğrulama), performans optimizasyonları ve izleme gibi konuları da derinlemesine araştırmanız gerekecektir.

Umarız bu rehber Go ile API geliştirme yolculuğunuzda size yardımcı olur. Mutlu kodlamalar!
Go Resmi Web Sitesi
Chi Router GitHub Sayfası
PostgreSQL Dokümantasyonu
Kod:
// Bu alan sadece örnek [code] etiketi kullanımı içindir.
// Gerçekte buraya ek kodlar veya ilgili bilgiler gelebilir.
// [b]Önemli Not:[/b] Kod blokları içindeki yorumlar da sayfa uzunluğuna katkıda bulunur.
 
shape1
shape2
shape3
shape4
shape5
shape6
Üst

Bu web sitenin performansı Hazal Host tarafından sağlanmaktadır.

YazilimForum.com.tr internet sitesi, 5651 sayılı Kanun’un 2. maddesinin 1. fıkrasının (m) bendi ve aynı Kanun’un 5. maddesi kapsamında Yer Sağlayıcı konumundadır. Sitede yer alan içerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır.

YazilimForum.com.tr, kullanıcılar tarafından paylaşılan içeriklerin doğruluğunu, güncelliğini veya hukuka uygunluğunu garanti etmez ve içeriklerin kontrolü veya araştırılması ile yükümlü değildir. Kullanıcılar, paylaştıkları içeriklerden tamamen kendileri sorumludur.

Hukuka aykırı içerikleri fark ettiğinizde lütfen bize bildirin: lydexcoding@gmail.com

Sitemiz, kullanıcıların paylaştığı içerik ve bilgileri 6698 sayılı KVKK kapsamında işlemektedir. Kullanıcılar, kişisel verileriyle ilgili haklarını KVKK Politikası sayfasından inceleyebilir.

Sitede yer alan reklamlar veya üçüncü taraf bağlantılar için YazilimForum.com.tr herhangi bir sorumluluk kabul etmez.

Sitemizi kullanarak Forum Kuralları’nı kabul etmiş sayılırsınız.

DMCA.com Protection Status Copyrighted.com Registered & Protected