Günümüz dijital dünyasında uygulamalar arası veri alışverişi hayati önem taşır. Peki, kendi uygulamanızın diğer sistemlerle sorunsuz bir şekilde konuşmasını nasıl sağlarsınız? İşte bu sorunun cevabı: RESTful API’ler. Bu kapsamlı rehberde, sıfırdan bir RESTful API oluşturmanın tüm adımlarını, temel kavramlardan ileri düzey ipuçlarına kadar keşfedeceğiz. Backend dünyasına ilk adımınızı atmaya hazır mısınız?
Modern uygulamaların vazgeçilmez bir bileşeni haline gelen API (Application Programming Interface), farklı yazılımların birbirleriyle iletişim kurmasını sağlayan bir köprü görevi görür. Örneğin, bir mobil uygulamanın hava durumu bilgisini alması, bir web sitesinin bir harita servisinden konum verilerini çekmesi veya bir bankacılık uygulamasının finansal işlemleri gerçekleştirmesi gibi senaryoların ardında hep API‘ler bulunur. Bu bağlamda, RESTful API‘ler, web servisleri dünyasında en yaygın ve popüler mimari yaklaşımlardan biridir.
REST (Representational State Transfer), web’in temelini oluşturan HTTP protokolünün basitliğini ve esnekliğini kullanarak tasarlanmış bir mimari stildir. Roy Fielding tarafından 2000 yılında doktora tezinde tanımlanan REST ilkelerine uygun olarak tasarlanan bir API‘ye “RESTful” denir. Peki, bu yaklaşım neden bu kadar kritik? RESTful API‘ler, uygulamaların daha ölçeklenebilir, performanslı ve bakımı kolay olmasını sağlar. Ayrıca, farklı platformlar ve teknolojiler arasında sorunsuz entegrasyon imkanı sunar.
Gerçek dünya uygulamalarında RESTful API‘lerin önemi yadsınamaz. Örneğin, büyük bir e-ticaret platformunu düşünelim. Bu platformun hem iOS hem de Android için mobil uygulamaları, bir web sitesi ve belki de üçüncü taraf iş ortakları için veri sağlayıcısı olması gerekebilir. İşte tam bu noktada, tüm bu farklı istemcilerin aynı RESTful API üzerinden ürün listelerine, kullanıcı profillerine ve sipariş detaylarına erişmesi, veri tutarlılığını sağlar ve geliştirme süreçlerini basitleştirir. Sosyal medya devleri (Twitter, Facebook) ve bulut hizmet sağlayıcıları (AWS, Google Cloud) gibi şirketler de, kendi platformlarına erişim sağlamak ve geliştirici ekosistemlerini genişletmek için RESTful API‘leri yoğun bir şekilde kullanır. Bu sayede, geliştiriciler kendi uygulamalarını bu platformlarla kolayca entegre edebilir ve yeni özellikler yaratabilirler. Bu durum, inovasyonu teşvik eder ve pazarın dinamizmini artırır.
RESTful API‘lerin temel bileşenleri ve ilkeleri şunlardır:
- Kaynaklar (Resources):
APIaracılığıyla erişilebilen tüm veriler veya işlevler bir kaynak olarak kabul edilir. Örneğin, “makaleler”, “kullanıcılar” veya “ürünler” birer kaynaktır. Her kaynak benzersiz birURI (Uniform Resource Identifier)ile temsil edilir. - HTTP Metotları (Verbs): Kaynaklar üzerinde hangi işlemin yapılacağını belirtmek için
HTTPprotokolünün standart metotları (GET,POST,PUT,DELETE) kullanılır. - Durumsuzluk (Statelessness): Her istek, sunucunun isteği işlemek için ihtiyaç duyduğu tüm bilgileri içermelidir. Sunucu, önceki isteklerden herhangi bir istemci bağlamını veya oturum bilgisini saklamaz. Bu ilke,
API‘lerin yatayda (horizontal) kolayca ölçeklenebilmesini sağlar. - İstemci-Sunucu (Client-Server): İstemci ve sunucu birbirinden bağımsız olarak geliştirilebilir ve evrimleşebilir. Bu ayrım, her iki tarafın da kendi teknolojisini ve tasarımını özgürce seçebilmesine olanak tanır.
- Katmanlı Sistem (Layered System):
API, önbellekler, yük dengeleyiciler veya güvenlik mekanizmaları gibi birden fazla katmanı içerebilir. İstemci genellikle doğrudan hangi katmanla etkileşimde olduğunu bilmek zorunda değildir. - Kodda Talep (Code-on-Demand – İsteğe Bağlı): (Opsiyonel) Sunucu, istemciye yürütülebilir kod (örneğin JavaScript) sağlayarak istemcinin işlevselliğini genişletebilir.
Bu ilkeler ve bileşenler, RESTful API‘lerin web dünyasında bu kadar güçlü ve yaygın olmasının temel nedenleridir. Bir sonraki bölümde, bu ilkelerin HTTP metotları aracılığıyla nasıl somutlaştırıldığını ve CRUD işlemlerini nasıl yürüttüğümüzü daha detaylı inceleyeceğiz.
RESTful İlkeleri Nelerdir ve HTTP Metotları Nasıl Kullanılır?
RESTful API tasarımının en temelinde, her şeyin bir “kaynak” olarak ele alınması yatar. Bir kaynak, URI (Uniform Resource Identifier) ile benzersiz bir şekilde tanımlanır. Örneğin, bir blog yazısı, bir kullanıcı profili veya bir ürün envanteri gibi kavramların her biri birer kaynaktır ve kendilerine özgü bir adresi vardır (örneğin, /api/urunler/123). Bu yaklaşım, API‘nizin yapısını son derece düzenli ve anlaşılır hale getirir. Kaynakların bu şekilde soyutlanması, istemcinin sunucu tarafındaki iç uygulamadan bağımsız olarak kaynaklarla etkileşime girmesine olanak tanır.
Kaynaklar üzerinde gerçekleştirilecek eylemler ise HTTP metotları aracılığıyla standart bir şekilde ifade edilir. Bu metotlar, temelde CRUD (Create, Read, Update, Delete) işlemlerini temsil eder ve API‘nin ne yapacağını net bir şekilde belirtir. Bu sayede, istemci ve sunucu arasında tutarlı ve tahmin edilebilir bir iletişim sağlanır. Örneğin, bir veri oluşturmak için POST, okumak için GET, güncellemek için PUT veya PATCH, silmek için ise DELETE metodu kullanılır.
HTTP Metotları ve CRUD İşlemleri Arasındaki İlişki
RESTful API‘lerde HTTP metotlarının CRUD işlemleriyle nasıl eşleştiğini aşağıdaki tablo özetlemektedir:
| HTTP Metodu | CRUD İşlemi | Açıklama | Örnek URI Kullanımı |
|---|---|---|---|
GET |
Read |
Belirtilen bir kaynaktan veya kaynak koleksiyonundan veri almak için kullanılır. Sunucuda hiçbir değişiklik yapmaz. | /api/kitaplar (tüm kitaplar)/api/kitaplar/42 (ID’si 42 olan kitap) |
POST |
Create |
Sunucuda yeni bir kaynak oluşturmak için kullanılır. İstek gövdesinde (request body) yeni kaynağın verilerini içerir. | /api/kitaplar (yeni bir kitap kaydı oluştur) |
PUT |
Update |
Var olan bir kaynağı tamamen güncellemek için kullanılır. İstek gövdesinde kaynağın güncel verisinin tamamı olmalıdır. | /api/kitaplar/42 (ID’si 42 olan kitabı tamamen güncelle) |
PATCH |
Update |
Var olan bir kaynağın sadece belirli bir kısmını (parçalı güncelleme) değiştirmek için kullanılır. | /api/kitaplar/42 (ID’si 42 olan kitabın sadece yazarını güncelle) |
DELETE |
Delete |
Belirtilen bir kaynağı sunucudan silmek için kullanılır. | /api/kitaplar/42 (ID’si 42 olan kitabı sil) |
Bu standartlaşmış yaklaşım, API‘lerin farklı sistemler ve geliştiriciler tarafından kolayca anlaşılmasını ve kullanılmasını sağlar. Ayrıca, REST‘in durumsuzluk (statelessness) ilkesi gereği, her HTTP isteği sunucuya kendi içinde bağımsız bir şekilde gönderilir. Bu, sunucunun önceki isteklerden herhangi bir istemci bağlamını hatırlamak zorunda kalmamasını sağlar. Sonuç olarak, API‘nizin yatayda ölçeklenmesi (yani daha fazla sunucu ekleyerek kapasite artırma) çok daha kolay hale gelir, çünkü herhangi bir sunucu herhangi bir isteği işleyebilir ve oturum verilerini yönetme yükü ortadan kalkar. Veri değişimi için genellikle JSON (JavaScript Object Notation) formatı tercih edilir, zira JSON hafifliği, okunabilirliği ve çeşitli programlama dilleriyle kolayca ayrıştırılabilmesi sayesinde web servislerinde en popüler veri formatı olmuştur. Tüm bu ilkeler ve metotlar, sağlam ve sürdürülebilir bir RESTful API oluşturmanız için size yol gösterecektir. Şimdi bu bilgileri pratiğe dökme zamanı!
İlk RESTful API’mizi Oluşturmaya Başlamak: Adım Adım Rehber
Temel RESTful kavramları ve HTTP metotlarını anladığımıza göre, artık kolları sıvayıp kendi ilk API‘mizi kodlamaya başlayabiliriz. Bu bölümde, basit bir makale yönetim sistemi için (makaleleri listeleyebileceğimiz, yeni makale ekleyebileceğimiz, güncelleyebileceğimiz ve silebileceğimiz) bir RESTful API‘yi adım adım Flask ve Flask-RESTful kullanarak nasıl hayata geçireceğimizi göreceğiz. Flask, Python tabanlı hafif bir web çatısıdır ve RESTful API‘ler geliştirmek için oldukça popülerdir. Başlamadan önce, sisteminizde Python ve pip’in kurulu olduğundan emin olun.
İlk adım olarak, projemiz için bir dizin oluşturalım ve gerekli kütüphaneleri kuralım. Terminalinizde şu komutları çalıştırın:
mkdir makale_api
cd makale_api
pip install Flask Flask-RESTful
Burada Flask-RESTful, Flask üzerine kurulu, RESTful API'leri daha deklaratif ve hızlı bir şekilde oluşturmanızı sağlayan bir uzantıdır. Şimdi, app.py adında bir dosya oluşturalım ve API'mizin iskeletini inşa edelim. Basitlik adına, şimdilik veritabanı yerine geçici olarak verileri bir Python listesinde tutacağız. Bu, API'nin temel CRUD işlevlerini anlamak için yeterli olacaktır.
from flask import Flask, request, jsonify
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
# Geçici veri kaynağı: Makaleleri bir Python listesinde tutuyoruz
makaleler = [
{"id": 1, "baslik": "RESTful API'ye Giriş", "icerik": "Bu makale RESTful API'lerin temellerini anlatıyor.", "yazar": "Ahmet Yılmaz"},
{"id": 2, "baslik": "Flask ile API Geliştirme", "icerik": "Python Flask framework'ü ile API nasıl yapılır.", "yazar": "Zeynep Demir"}
]
# Yeni makaleler için ID atamak üzere basit bir sayaç
next_id = 3
# Tek bir makale kaynağı için sınıf
class Makale(Resource):
def get(self, makale_id):
# Belirli bir makaleyi ID'ye göre getir
for makale in makaleler:
if makale['id'] == makale_id:
return makale, 200 # Başarılı yanıt, 200 OK
return {"mesaj": "Makale bulunamadı."}, 404 # 404 Not Found
def put(self, makale_id):
# Belirli bir makaleyi ID'ye göre güncelle
global makaleler # Listeyi global olarak değiştiriyoruz
data = request.get_json() # İstemciden gelen JSON verisini al
for index, makale in enumerate(makaleler):
if makale['id'] == makale_id:
makaleler[index] = {"id": makale_id, "baslik": data['baslik'], "icerik": data['icerik'], "yazar": data['yazar']}
return {"mesaj": "Makale güncellendi.", "makale": makaleler[index]}, 200
return {"mesaj": "Makale bulunamadı."}, 404
def delete(self, makale_id):
# Belirli bir makaleyi ID'ye göre sil
global makaleler
makaleler = [makale for makale in makaleler if makale['id'] != makale_id]
return {"mesaj": "Makale silindi."}, 204 # 204 No Content (içerik yok, başarılı silme)
# Tüm makaleleri listelemek ve yeni makale oluşturmak için sınıf
class MakaleListesi(Resource):
def get(self):
# Tüm makaleleri döndür
return makaleler, 200
def post(self):
# Yeni bir makale oluştur
global next_id
data = request.get_json()
# Temel doğrulama
if not data or 'baslik' not in data or 'icerik' not in data or 'yazar' not in data:
return {"mesaj": "Gerekli alanlar eksik (başlık, içerik, yazar)."}, 400 # 400 Bad Request
yeni_makale = {
"id": next_id,
"baslik": data['baslik'],
"icerik": data['icerik'],
"yazar": data['yazar']
}
makaleler.append(yeni_makale)
next_id += 1
return {"mesaj": "Makale başarıyla eklendi.", "makale": yeni_makale}, 201 # 201 Created
# API rotalarını tanımlama
api.add_resource(MakaleListesi, '/api/makaleler') # GET (tümü), POST (yeni)
api.add_resource(Makale, '/api/makaleler/') # GET, PUT, DELETE (belirli bir makale)
if __name__ == '__main__':
app.run(debug=True)
Yukarıdaki kodda, iki farklı Resource sınıfı tanımladık: MakaleListesi (tüm makaleler için) ve Makale (belirli bir makale için). Her sınıf, ilgili HTTP metotlarına (GET, POST, PUT, DELETE) karşılık gelen fonksiyonları içerir. Örneğin, MakaleListesi sınıfındaki get metodu tüm makaleleri döndürürken, post metodu yeni bir makale ekler. Makale sınıfı ise belirli bir ID'ye sahip makale üzerinde işlem yapar. Her fonksiyonda, işlemin sonucunu belirten standart HTTP status code'lar (örn. 200 OK, 404 Not Found, 201 Created) döndürüyoruz. Bu durum kodları, API'nizi tüketen diğer uygulamaların doğru şekilde tepki vermesi için hayati öneme sahiptir.
API'mizi çalıştırmak için terminalinizde python app.py komutunu çalıştırın. Uygulamanız genellikle http://127.0.0.1:5000 adresinde çalışacaktır. API'nizi test etmek için Postman gibi bir API istemcisi veya curl komutunu kullanabilirsiniz. İşte bazı örnekler:
- Tüm makaleleri almak (
GET):curl http://127.0.0.1:5000/api/makaleler - Yeni bir makale eklemek (
POST):curl -X POST -H "Content-Type: application/json" -d '{"baslik": "Yeni Makale Başlığı", "icerik": "Bu bir deneme makalesidir.", "yazar": "Deniz Aydın"}' http://127.0.0.1:5000/api/makaleler - ID'si 1 olan makaleyi güncellemek (
PUT):curl -X PUT -H "Content-Type: application/json" -d '{"baslik": "Güncel Makale Başlığı", "icerik": "Güncellenmiş içerik burada.", "yazar": "Ahmet Yılmaz"}' http://127.0.0.1:5000/api/makaleler/1 - ID'si 2 olan makaleyi silmek (
DELETE):curl -X DELETE http://127.0.0.1:5000/api/makaleler/2
Bu adımlar, size temel bir RESTful API'nin nasıl kurulduğunu göstermektedir. Ancak unutmayın, gerçek dünya uygulamalarında verilerin kalıcı olması için bir veritabanına ihtiyacımız var. Bir sonraki bölümde, API'mizi bir veritabanıyla nasıl entegre edeceğimizi ve verilerimizi daha sağlam bir şekilde nasıl yöneteceğimizi inceleyeceğiz.
Veritabanı Entegrasyonu ve Veri Modelleme: API'nizin Kalbi
Önceki bölümde, verileri geçici olarak bir Python listesinde tutarak ilk RESTful API'mizi oluşturduk. Ancak bu yaklaşım, uygulamanız yeniden başlatıldığında tüm verilerin kaybolması anlamına gelir. Gerçek dünya uygulamaları için verilerin kalıcı olarak saklanması ve daha karmaşık sorguların yapılabilmesi adına bir veritabanı entegrasyonu şarttır. Veritabanı, API'nizin adeta kalbidir; tüm veriler buradan okunur ve buraya yazılır. Bu bölümde, Python ve Flask ekosisteminde yaygın olarak kullanılan SQLAlchemy kütüphanesini ve Flask için onun eklentisi olan Flask-SQLAlchemy'yi kullanarak, API'mizi basit bir SQLite veritabanına nasıl bağlayacağımızı göstereceğiz.
SQLite, kurulumu kolay ve dosya tabanlı çalışan hafif bir ilişkisel veritabanı olduğu için başlangıç projeleri için idealdir. Daha büyük ölçekli uygulamalar için PostgreSQL veya MySQL gibi veritabanlarına geçiş yapmak da benzer SQLAlchemy prensipleriyle mümkündür. İlk olarak, gerekli kütüphaneleri kuralım:
pip install Flask-SQLAlchemy
Şimdi app.py dosyamızı güncelleyerek veritabanı modelimizi tanımlayalım ve CRUD işlemlerini bu model üzerinden gerçekleştirelim. Bu sayede, verilerimiz artık kalıcı olacak ve uygulamamızın dayanıklılığı artacak.
from flask import Flask, request, jsonify
from flask_restful import Resource, Api, reqparse
from flask_sqlalchemy import SQLAlchemy
import os
app = Flask(__name__)
# SQLite veritabanı dosyasının yolu
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///makaleler.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Gereksiz uyarıları kapatır
db = SQLAlchemy(app)
api = Api(app)
# Veritabanı modeli: MakaleModel
class MakaleModel(db.Model):
__tablename__ = 'makaleler' # Veritabanı tablosunun adı
id = db.Column(db.Integer, primary_key=True)
baslik = db.Column(db.String(120), nullable=False)
icerik = db.Column(db.Text, nullable=False)
yazar = db.Column(db.String(80), nullable=False)
def __init__(self, baslik, icerik, yazar):
self.baslik = baslik
self.icerik = icerik
self.yazar = yazar
# Makale objesini JSON formatına dönüştüren metod
def json(self):
return {'id': self.id, 'baslik': self.baslik, 'icerik': self.icerik, 'yazar': self.yazar}
# Veritabanı tablolarını otomatik olarak oluştur (uygulama ilk çalıştığında)
@app.before_first_request
def create_tables():
db.create_all()
# Gelen isteklerdeki argümanları doğrulamak ve ayrıştırmak için parser
parser = reqparse.RequestParser()
parser.add_argument('baslik', type=str, required=True, help="Başlık alanı zorunludur.")
parser.add_argument('icerik', type=str, required=True, help="İçerik alanı zorunludur.")
parser.add_argument('yazar', type=str, required=True, help="Yazar alanı zorunludur.")
# Makale Kaynağı (Veritabanı ile entegre)
class Makale(Resource):
def get(self, makale_id):
makale = MakaleModel.query.filter_by(id=makale_id).first() # ID'ye göre makale bul
if makale:
return makale.json(), 200
return {'mesaj': 'Makale bulunamadı.'}, 404
def put(self, makale_id):
data = parser.parse_args() # Doğrulanmış veriyi al
makale = MakaleModel.query.filter_by(id=makale_id).first()
if makale:
makale.baslik = data['baslik']
makale.icerik = data['icerik']
makale.yazar = data['yazar']
db.session.commit() # Değişiklikleri veritabanına kaydet
return makale.json(), 200
return {'mesaj': 'Makale bulunamadı.'}, 404
def delete(self, makale_id):
makale = MakaleModel.query.filter_by(id=makale_id).first()
if makale:
db.session.delete(makale) # Makaleyi sil
db.session.commit()
return {'mesaj': 'Makale silindi.'}, 204
return {'mesaj': 'Makale bulunamadı.'}, 404
# Makale Listesi Kaynağı (Veritabanı ile entegre)
class MakaleListesi(Resource):
def get(self):
return [makale.json() for makale in MakaleModel.query.all()], 200 # Tüm makaleleri JSON olarak döndür
def post(self):
data = parser.parse_args() # Doğrulanmış veriyi al
makale = MakaleModel(data['baslik'], data['icerik'], data['yazar'])
db.session.add(makale) # Yeni makaleyi veritabanına ekle
db.session.commit()
return makale.json(), 201
# API rotalarını tanımlama (öncekiyle aynı)
api.add_resource(MakaleListesi, '/api/makaleler')
api.add_resource(Makale, '/api/makaleler/')
if __name__ == '__main__':
app.run(debug=True)
Bu güncellenmiş kodda, MakaleModel adında bir SQLAlchemy modeli tanımladık. Bu model, veritabanındaki makaleler tablosunu temsil eder ve makalelerimizin id, baslik, icerik ve yazar alanlarını nasıl saklanacağını belirtir. @app.before_first_request dekoratörü sayesinde, uygulama ilk çalıştığında makaleler.db dosyası ve içindeki tablo otomatik olarak oluşturulur. Artık API çağrıları, MakaleModel aracılığıyla veritabanı ile etkileşime girer ve verilerimiz kalıcı hale gelir.
reqparse.RequestParser kullanımı, gelen HTTP isteklerinin gövdesindeki verileri otomatik olarak doğrulamamızı sağlar. Bu sayede, API'ye gönderilen verilerin doğru formatta ve eksiksiz olup olmadığını kolayca kontrol edebiliriz. Örneğin, bir POST isteği gönderildiğinde başlık, içerik ve yazar alanlarının mutlaka olmasını bekleriz. Eğer bu alanlardan biri eksikse, Flask-RESTful otomatik olarak 400 Bad Request hata yanıtı döndürerek istemciyi bilgilendirir. Bu, API'nizin hem güvenliğini hem de veri tutarlılığını artıran çok önemli bir adımdır. Veritabanı entegrasyonu tamamlandığında, API'niz çok daha güçlü ve kullanışlı hale gelir. Bir sonraki bölümde, bu sağlam yapıyı daha da ileri taşıyarak, API'nizi yetkisiz erişimden korumak için kimlik doğrulama ve yetkilendirme mekanizmalarını ele alacağız.
API Güvenliği: Kimlik Doğrulama ve Yetkilendirme Nasıl Yapılır?
Herhangi bir RESTful API geliştirirken, güvenlik en öncelikli konulardan biridir. API'nizin herkes tarafından açıkça erişilebilir olması, genellikle hassas verilerin açığa çıkmasına veya yetkisiz işlemlerin yapılmasına yol açabilir. Bu nedenle, kimlik doğrulama (authentication) ve yetkilendirme (authorization) mekanizmalarını doğru bir şekilde uygulamak kritik öneme sahiptir. Kimlik doğrulama, bir kullanıcının veya uygulamanın "kim olduğunu" doğrulamakla ilgilenirken; yetkilendirme ise, doğrulanmış bir kullanıcının "ne yapmaya izni olduğunu" belirler.
RESTful API'ler için en popüler ve ölçeklenebilir kimlik doğrulama yöntemlerinden biri Token Tabanlı Kimlik Doğrulama'dır. Bu yöntemde, sunucu istemci tarafında herhangi bir oturum bilgisi tutmadığı için REST'in durumsuzluk ilkesiyle mükemmel bir uyum sağlar. Bu mekanizma genellikle JWT (JSON Web Tokens) kullanılarak uygulanır ve adımları şöyledir:
- Kullanıcı, kullanıcı adı ve şifresiyle
API'ye bir giriş (login) isteği gönderir. API, gelen kimlik bilgilerini doğrular. Başarılı olması durumunda, istemciye dijital olarak imzalanmış birJWTdöner. Butoken, kullanıcının kimliğini ve belirli yetkileri (rolleri) içeren güvenli bir dizedir.- İstemci, sonraki her
APIisteğinde buJWT'yiHTTPbaşlıklarında (Authorization: Bearerformatında) sunucuya gönderir. - Sunucu, gelen her istekte
token'ı doğrular ve kullanıcının istekte bulunulan kaynağa erişme veya eylem gerçekleştirme iznine sahip olup olmadığını kontrol eder.
Bu işlevselliği Flask uygulamamıza eklemek için Flask-JWT-Extended kütüphanesini kullanabiliriz. Kurulumu oldukça basittir:
pip install Flask-JWT-Extended
Şimdi app.py dosyamızı güncelleyerek bir kullanıcı modeli, kayıt ve giriş endpointleri ekleyelim ve Makale kaynaklarımızı koruma altına alalım. Basitlik adına, şimdilik şifreleri veritabanında düz metin olarak saklayacağız, ancak gerçek uygulamalarda şifrelerin mutlaka hashlenmesi (örneğin bcrypt kütüphanesi ile) gerektiğini unutmayın.
from flask import Flask, request, jsonify
from flask_restful import Resource, Api, reqparse
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import create_access_token, JWTManager, jwt_required, get_jwt_identity
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///makaleler.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['JWT_SECRET_KEY'] = 'sizin-cok-gizli-anahtariniz' # JWT token'larını imzalamak için gizli anahtar
db = SQLAlchemy(app)
api = Api(app)
jwt = JWTManager(app) # Flask-JWT-Extended uzantısını başlat
# ... (MakaleModel sınıfı ve mevcut create_tables fonksiyonu aynen kalır) ...
# Kullanıcı modeli
class UserModel(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(80), nullable=False) # Gerçekte hashlenmeli!
def __init__(self, username, password):
self.username = username
self.password = password
def json(self):
return {'id': self.id, 'username': self.username}
# Tabloları oluştur ve ilk kullanıcıyı ekle (uygulama ilk çalıştığında)
@app.before_first_request
def create_tables():
db.create_all()
# Örnek bir kullanıcı ekle (sadece veritabanı boşsa)
if not UserModel.query.filter_by(username='admin').first():
admin_user = UserModel(username='admin', password='password') # Şifre hashlenmeli
db.session.add(admin_user)
db.session.commit()
# Kullanıcı kayıt ve giriş için istek argümanı parser'ı
user_parser = reqparse.RequestParser()
user_parser.add_argument('username', type=str, required=True, help="Kullanıcı adı zorunludur.")
user_parser.add_argument('password', type=str, required=True, help="Şifre zorunludur.")
# Kullanıcı Kayıt Kaynağı
class UserRegister(Resource):
def post(self):
data = user_parser.parse_args()
if UserModel.query.filter_by(username=data['username']).first():
return {"mesaj": "Bu kullanıcı adı zaten mevcut."}, 400
user = UserModel(data['username'], data['password'])
db.session.add(user)
db.session.commit()
return {"mesaj": "Kullanıcı başarıyla kaydedildi."}, 201
# Kullanıcı Giriş Kaynağı
class UserLogin(Resource):
def post(self):
data = user_parser.parse_args()
user = UserModel.query.filter_by(username=data['username']).first()
if user and user.password == data['password']: # Gerçekte hashlenmiş şifre kontrolü yapılmalı
# Başarılı giriş, JWT oluştur ve döndür
access_token = create_access_token(identity=user.id, expires_delta=False) # expires_delta ile token ömrü belirlenebilir
return {"access_token": access_token}, 200
return {"mesaj": "Geçersiz kullanıcı adı veya şifre."}, 401 # 401 Unauthorized
# Makale Kaynağı (Güvenlik Eklendi)
class Makale(Resource):
@jwt_required() # Bu endpoint'e erişim için geçerli bir JWT gereklidir
def get(self, makale_id):
current_user_id = get_jwt_identity() # Token içindeki kullanıcı ID'sini al
makale = MakaleModel.query.filter_by(id=makale_id).first()
if makale:
# Örnek yetkilendirme: Sadece kendi makalesini görebilir (basit örnek)
# Eğer makale.yazar ile mevcut kullanıcının yazar adı eşleşiyorsa
return makale.json(), 200
return {'mesaj': 'Makale bulunamadı.'}, 404
@jwt_required()
def put(self, makale_id):
current_user_id = get_jwt_identity()
current_username = UserModel.query.filter_by(id=current_user_id).first().username
data = parser.parse_args()
makale = MakaleModel.query.filter_by(id=makale_id).first()
if makale:
# Yetkilendirme örneği: Sadece makale yazarı makaleyi güncelleyebilir
if makale.yazar == current_username:
makale.baslik = data['baslik']
makale.icerik = data['icerik']
# Yazar alanı genellikle güncellenemez veya sadece admin tarafından güncellenir
# makale.yazar = data['yazar']
db.session.commit()
return makale.json(), 200
return {"mesaj": "Bu makaleyi güncelleme yetkiniz yok."}, 403 # 403 Forbidden
return {'mesaj': 'Makale bulunamadı.'}, 404
@jwt_required()
def delete(self, makale_id):
current_user_id = get_jwt_identity()
current_username = UserModel.query.filter_by(id=current_user_id).first().username
makale = MakaleModel.query.filter_by(id=makale_id).first()
if makale:
if makale.yazar == current_username:
db.session.delete(makale)
db.session.commit()
return {'mesaj': 'Makale silindi.'}, 204
return {"mesaj": "Bu makaleyi silme yetkiniz yok."}, 403
return {'mesaj': 'Makale bulunamadı.'}, 404
# Makale Listesi Kaynağı (Güvenlik Eklendi - POST işlemi için)
class MakaleListesi(Resource):
def get(self): # Tüm makaleleri herkes görebilir
return [makale.json() for makale in MakaleModel.query.all()], 200
@jwt_required() # Yeni makale oluşturmak için JWT gerekli
def post(self):
current_user_id = get_jwt_identity()
current_username = UserModel.query.filter_by(id=current_user_id).first().username
data = parser.parse_args()
# Yeni makale oluştururken yazar alanını otomatik olarak token sahibi yaparız
makale = MakaleModel(data['baslik'], data['icerik'], current_username)
db.session.add(makale)
db.session.commit()
return makale.json(), 201
# API rotalarını tanımlama (yeni güvenlik endpointleri eklendi)
api.add_resource(MakaleListesi, '/api/makaleler')
api.add_resource(Makale, '/api/makaleler/')
api.add_resource(UserRegister, '/register')
api.add_resource(UserLogin, '/login')
if __name__ == '__main__':
app.run(debug=True)
Bu güncellenmiş kodda, UserModel ile basit bir kullanıcı veritabanı modeli oluşturduk ve UserRegister ile yeni kullanıcı kaydı, UserLogin ile de kullanıcı girişi için endpointler tanımladık. Giriş başarılı olursa, create_access_token fonksiyonu ile bir JWT oluşturulur ve istemciye geri gönderilir. @jwt_required() dekoratörünü kullanarak, Makale kaynağındaki get, put, delete metotlarını ve MakaleListesi kaynağındaki post metodunu koruma altına aldık. Yani, bu endpointlere erişmek için geçerli bir JWT göndermek zorunlu hale geldi. get_jwt_identity() fonksiyonu ile token içindeki kullanıcı kimliğini alıp, yetkilendirme kontrollerinde kullanabiliriz. Örneğin, bir makaleyi yalnızca o makalenin yazarı güncelleyebilir veya silebilir. Gerçek uygulamalarda şifrelerin güvenli bir şekilde hashlenmesi ve rol tabanlı yetkilendirme gibi daha gelişmiş güvenlik önlemleri almanız, API'nizin uzun vadeli güvenliği için hayati önem taşır.
Hata Yönetimi ve Sürümleme: Sağlam Bir API İçin İleri Düzey Konular
Bir RESTful API geliştirirken, temel işlevselliği sağlamak kadar, API'nizin sağlam, sürdürülebilir ve geliştirici dostu olmasını sağlamak da büyük önem taşır. Bu bağlamda, hata yönetimi (error handling) ve sürümleme (versioning) gibi ileri düzey konulara dikkat etmek, API'nizin kalitesini ve kullanım kolaylığını doğrudan etkiler. Bu unsurlar, uygulamanızın beklenmedik durumlarla nasıl başa çıktığını belirler ve gelecekteki değişiklikleri sorunsuz bir şekilde yönetmenizi sağlar.
Hata Yönetimi: API'nizi Güçlendirme
İyi bir hata yönetimi, API tüketicilerinin (yani API'nizi kullanan diğer uygulamaların geliştiricileri) sorunları daha hızlı teşhis etmelerine ve entegrasyonlarını daha sağlam hale getirmelerine yardımcı olur. Temel olarak, HTTP durum kodlarını (status codes) doğru kullanmak, hata yönetiminin ilk ve en önemli adımıdır. Ancak sadece durum kodu döndürmek yeterli değildir; hata durumlarında JSON formatında açıklayıcı hata mesajları da döndürmek, geliştirici deneyimini önemli ölçüde artırır. Örneğin:
{
"mesaj": "Makale bulunamadı",
"kod": 404,
"detaylar": "İstenen ID'ye sahip makale veritabanında mevcut değil. Lütfen ID'yi kontrol edin."
}
Yaygın HTTP hata kodları şunlardır:
400 Bad Request: İstemcinin gönderdiği istek formatı yanlış veya eksik (örneğin, zorunlu alanlar boş).401 Unauthorized: İstemci kimlik doğrulama yapmadı veya geçersiz kimlik bilgileri sundu (JWT eksik/geçersiz).403 Forbidden: İstemci kimliği doğrulandı ancak istenen kaynağa erişim veya işlem yapma yetkisi yok.404 Not Found: İstenen kaynakAPI'de mevcut değil (yanlışURIveya ID).500 Internal Server Error: Sunucu tarafında beklenmedik bir hata oluştu. GenellikleAPIgeliştiricisi için bir sorun olduğunu gösterir.
Flask-RESTful gibi çatılar, errors modülü aracılığıyla özel hata işleyicileri tanımlamanıza olanak tanır. Böylece, uygulamanızdaki çeşitli istisnaları (örneğin, veritabanı bağlantı hataları, doğrulama hataları) merkezi bir şekilde yakalayabilir ve tutarlı hata yanıtları döndürebilirsiniz.
Sürümleme (Versioning): API'nizin Evrimi
Bir API, tıpkı diğer yazılımlar gibi zamanla gelişir. Yeni özellikler eklenir, mevcut endpointler değişir veya bazen kaldırılır. Bu değişiklikler, API'nizi kullanan mevcut istemcilerin (mobil uygulamalar, web siteleri vb.) bozulmasına yol açabilir. Sürümleme, bu sorunu çözmek ve geriye dönük uyumluluğu koruyarak API'nizi gelecekteki değişikliklere hazır hale getirmek için kullanılır. Popüler sürümleme stratejileri şunlardır:
- URI Sürümleme: Sürüm numarasını doğrudan
URI'ye dahil etmek en yaygın ve anlaşılır yöntemlerden biridir./api/v1/makaleler /api/v2/makalelerBu yaklaşım, her sürüm için ayrı rotalar oluşturmayı gerektirir, ancak istemciler için oldukça nettir.
- Query Parametre Sürümleme: Sürüm numarasını bir sorgu parametresi olarak göndermek.
/api/makaleler?version=1 /api/makaleler?version=2Bu yöntem daha esnektir, ancak
URI'leri daha az açıklayıcı hale getirebilir ve önbellekleme sorunlarına yol açabilir. - Header Sürümleme: Özel bir
HTTPbaşlığı (X-API-Versiongibi) kullanarak sürüm bilgisini iletmek.GET /api/makaleler X-API-Version: 1Bu yöntem,
URI'yi temiz tutar ve istemcinin hangi sürümü istediğini açıkça belirtir.
Hangi sürümleme yöntemini seçerseniz seçin, tutarlı olmak ve API dokümantasyonunuzda bu stratejiyi açıkça belirtmek çok önemlidir. Örneğin, URI sürümlemesini seçtiyseniz, ilk kod örneğimizdeki api.add_resource çağrılarını /api/v1/makaleler şeklinde değiştirebilirsiniz. Bu, gelecekte /api/v2/makaleler gibi yeni bir sürüm geliştirdiğinizde mevcut istemcilerinizi etkilemeden değişiklik yapabilmenizi sağlar. Güçlü hata yönetimi ve akıllı sürümleme pratikleri, API'nizin uzun ömürlü, profesyonel ve kolayca benimsenen bir çözüm olmasını sağlar.
Sonuç: Backend Büyücülüğü Yolculuğunda Bir Sonraki Adımlar
Bu kapsamlı rehber boyunca, RESTful API'lerin temel kavramlarından başlayarak, pratik bir Flask uygulaması ile veritabanı entegrasyonuna, hatta güvenlik ve sürümleme gibi ileri düzey konulara kadar geniş bir yelpazede bilgi edindik. Artık kendi Backend sihrinizi yaratmaya hazırsınız! Bir RESTful API inşa etmek, modern uygulama geliştirmenin temel taşlarından biridir ve bu yolculukta attığınız ilk adımlar sizi gelecekteki karmaşık projelere hazırlayacaktır.
Başlangıçta basit bir liste ile verileri yönetirken, zamanla SQLAlchemy ile veritabanı entegrasyonu yaparak API'mizin kalıcılığını sağladık. Ardından, Flask-JWT-Extended ile güvenlik katmanını ekleyerek kimlik doğrulama ve yetkilendirme mekanizmalarını öğrendik. Son olarak, hata yönetimi ve sürümleme gibi konularla API'nizin sağlamlığını ve geleceğe hazır olmasını nasıl sağlayacağımızı keşfettik. Her bir adım, sizi daha yetkin bir Backend geliştiricisi yapma yolunda önemli birer kilometre taşıdır. Unutmayın, pratik yapmak ve gerçek dünya projeleri üzerinde çalışmak, öğrenmenin en etkili yoludur. Bu rehberdeki bilgileri kendi projelerinizde uygulayarak deneyim kazanmaya devam edin.
Bir Backend büyücüsü olma yolculuğu, sürekli öğrenme ve keşif gerektiren bir süreçtir. Bu rehber sadece bir başlangıç noktasıdır. API'nizi daha da optimize etmek için önbellekleme (caching), asenkron görevler (Celery gibi), API dokümantasyonu (Swagger/OpenAPI), CI/CD (Continuous Integration/Continuous Deployment) süreçleri ve dağıtım (deployment) stratejileri gibi konuları araştırmayı düşünebilirsiniz. Bu ileri düzey teknikler, API'nizin performansını artıracak, bakımını kolaylaştıracak ve geliştirici ekibinizin verimliliğini yükseltecektir. Her zaman en iyi uygulamaları takip etmeye özen gösterin ve açık kaynak topluluklarının sunduğu kaynaklardan faydalanmaktan çekinmeyin. Unutmayın, en iyi kod, sürekli iyileştirilen ve öğrenilen koddur!
Sıkça Sorulan Sorular (SSS)
1. RESTful API'ler sadece JSON mı kullanır?
Hayır, RESTful API'ler teknik olarak herhangi bir veri formatını kullanabilir. XML, YAML veya hatta düz metin bile kullanılabilir. Ancak, hafifliği, okunabilirliği ve modern web teknolojileriyle uyumluluğu nedeniyle JSON (JavaScript Object Notation) günümüzde açık ara en popüler ve önerilen formattır. Geliştiriciler arasında yaygın kabul görmüş bir standart haline gelmiştir.
2. RESTful API ile SOAP API arasındaki temel fark nedir?
RESTful API'ler daha hafif, esnek ve HTTP protokolünü doğrudan kullanarak kaynak tabanlı çalışırken; SOAP (Simple Object Access Protocol) API'ler daha katı, protokole dayalıdır ve genellikle XML tabanlı mesajlaşma kullanır. SOAP, daha çok kurumsal düzeydeki entegrasyonlar ve gelişmiş güvenlik standartları (WS-Security) için tercih edilirken, REST daha çok web ve mobil uygulamalar için popülerdir çünkü daha az kaynak tüketir ve uygulaması daha basittir.
3. API'mi mobil uygulamalar için geliştirirken nelere dikkat etmeliyim?
Mobil uygulamalar için API geliştirirken performans, pil ömrü ve veri tüketimi öncelikli olmalıdır. Bu nedenle, yanıt boyutunu minimize etmek için verimli JSON kullanımı, sıkıştırma (Gzip gibi), önbellekleme stratejileri ve sadece gerekli veriyi döndüren endpointler (örneğin, GraphQL kullanımı veya kısmi yanıtlar) önemlidir. Ayrıca, güçlü kimlik doğrulama (JWT gibi) ve hata yönetimi mekanizmaları da mobil deneyim için kritik öneme sahiptir.
4. Bir RESTful API'yi nasıl test edebilirim?
RESTful API'leri test etmek için çeşitli araçlar mevcuttur. Postman, Insomnia gibi GUI tabanlı araçlar, istekleri oluşturmayı ve yanıtları görsel olarak incelemeyi kolaylaştırır. Komut satırında curl veya HTTPie gibi araçlar da kullanılabilir. Otomatik testler için Python'da unittest veya pytest, Node.js'de Mocha veya Jest gibi test çatılarından faydalanabilirsiniz. Bu testler, API'nizin beklendiği gibi çalıştığını ve geriye dönük uyumluluğun korunduğunu doğrular.
5. API'min performansını nasıl artırabilirim?
API performansını artırmak için birkaç yaklaşım vardır. İlk olarak, veritabanı sorgularınızı optimize edin ve indeksler kullanın. İkinci olarak, sıkça istenen veriler için önbellekleme (caching) mekanizmaları uygulayın (örneğin, Redis ile). Üçüncü olarak, yoğun işlem gerektiren görevleri ana istek döngüsünden ayırarak asenkron olarak çalıştırın (örneğin, mesaj kuyrukları ve Celery gibi araçlarla). Dördüncü olarak, yanıt boyutlarını küçültmek için sıkıştırma kullanın ve sadece gerekli veriyi döndürün. Son olarak, API'nizi yatayda ölçekleyebilecek bir mimari (load balancing ile birden fazla sunucu) tasarlayın ve kodunuzdaki darboğazları profilleyerek tespit edin.