İçeriğe geç
Muhammet Şafak
Web Geliştirme 3 dk okuma

Laravel ile JSON API uçları yazmak

Laravel'de arayüzden bağımsız JSON API uç noktaları oluşturmanın pratik yollarını anlattım.


Laravel ile Blade şablonları üretmek yerine JSON döndürmeye geçtiğimde ilk zorluk “bunu nasıl yapılandıracağım?” oldu. Formların action’larına POST atılan, redirect ile yönlendirilen klasik web uygulamasından farklı bir düşünce biçimi gerektiriyor. İstemci artık bir tarayıcı değil; belki mobil uygulama, belki başka bir servis, belki de AJAX isteği atan bir JavaScript kodu.

Bu yazıda Laravel 5 ile JSON API uç noktalarını (endpoint) nasıl yazdığımı, yanıt yapısını nasıl düzenlediğimi ve sık karşılaştığım durumları nasıl ele aldığımı anlatacağım.

Route tanımlamak

Laravel 5’te API rotaları (route) için app/Http/routes.php içinde ayrı bir gruba almak iyi bir alışkanlık. Hem URL öneki (/api/v1/...) eklemeyi kolaylaştırıyor hem de middleware uygulamayı düzenliyor.

Route::group(['prefix' => 'api/v1'], function () {
    Route::get('orders',         'Api\OrderController@index');
    Route::post('orders',        'Api\OrderController@store');
    Route::get('orders/{id}',    'Api\OrderController@show');
    Route::put('orders/{id}',    'Api\OrderController@update');
    Route::delete('orders/{id}', 'Api\OrderController@destroy');
});

Api\ namespace’i, API controller’larını normal web controller’larından ayırmak için kullanıyorum. app/Http/Controllers/Api/ altındaki dosyalar bu prefix’i alıyor.

JSON yanıt döndürmek

Laravel’de JSON yanıtı döndürmek için response()->json() kullanıyorsunuz. İkinci parametre durum kodunu belirliyor.

class OrderController extends Controller
{
    public function index()
    {
        $orders = Order::all();

        return response()->json([
            'data' => $orders,
        ], 200);
    }

    public function store(Request $request)
    {
        $order = Order::create($request->only('product_id', 'quantity'));

        return response()->json([
            'data' => $order,
        ], 201);
    }

    public function show($id)
    {
        $order = Order::find($id);

        if (!$order) {
            return response()->json([
                'message' => 'Sipariş bulunamadı.',
            ], 404);
        }

        return response()->json([
            'data' => $order,
        ], 200);
    }
}

Bir konuya dikkat ettim: her yanıtta data anahtarını kullanmak. Başta “neden bu katmanı ekleyeyim?” diye düşündüm ama istemci tarafında yanıt yapısı tutarlı olunca “buradaki data benim verimsem hata da ayrı bir anahtarda” şeklinde net bir sözleşme oluşuyor.

Hata yanıtlarını standartlaştırmak

Uygulama genelinde hata yanıtlarını tek bir biçimde döndürmek, istemciyi yazan kişinin işini kolaylaştırıyor. Tek bir yardımcı metod yeterli:

protected function hataDon($mesaj, $kod = 400): \Illuminate\Http\JsonResponse
{
    return response()->json([
        'error'   => true,
        'message' => $mesaj,
    ], $kod);
}

Sonra controller içinde return $this->hataDon('Geçersiz veri.', 422); şeklinde kullanıyorsunuz. Her controller’da ayrı format üretmiyorsunuz.

Bunu bir temel controller’a koymak yerine ayrı bir trait’e taşımak daha temiz olabiliyor. Böylece bu davranışa ihtiyaç duyan her controller’a eklenebiliyor, zorunlu bir kalıtım hiyerarşisi oluşturulmuyor.

Validation ve API

Laravel’in validation sistemi normalde hata durumunda redirect yapıyor. API’de redirect değil; JSON hata yanıtı istiyoruz.

FormRequest sınıfı burada işe yarıyor. Bir StoreOrderRequest oluşturup wantsJson() kontrolünü kaldırırsanız Laravel 5, JSON isteği için otomatik olarak 422 durum koduyla JSON hata yanıtı dönüyor.

class StoreOrderRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'product_id' => 'required|integer|exists:products,id',
            'quantity'   => 'required|integer|min:1',
        ];
    }
}

Controller’da tip belirtmek yeterli:

public function store(StoreOrderRequest $request)
{
    // Bu noktaya gelindiyse doğrulama geçti demektir
    $order = Order::create($request->only('product_id', 'quantity'));

    return response()->json(['data' => $order], 201);
}

Doğrulama başarısız olduğunda Laravel otomatik olarak şöyle bir yanıt üretiyor:

{
  "product_id": ["Ürün alanı zorunludur."],
  "quantity": ["Miktar alanı zorunludur."]
}

422 durum koduyla.

Content-Type başlığını doğru ayarlamak

İstemci tarafında JSON gönderirken Content-Type: application/json başlığını eklemeyi unutmamak gerekiyor. Laravel bu başlığa bakarak isteği JSON olarak ayrıştırıyor. $request->input() ile değerlere ulaşabilmek için bu önemli.

Bu başlık eksik gönderildiğinde Laravel isteği form verisi (form-data) olarak işliyor. JSON gövdesi gönderdiniz ama $request->input('product_id') null dönüyor — ve nedenini anlamak biraz zaman alıyor. Bunu Postman ile test ederken mutlaka Content-Type: application/json başlığını ve Accept: application/json başlığını eklemek gerekiyor. İkincisi Laravel’in doğrulama hatasını JSON olarak döndürmesini sağlıyor; eksik bırakırsanız HTML hata sayfası alabilirsiniz.

İlk gerçek API deneyimi

Bu yöntemi kullanarak küçük bir sipariş API’si yazdığımda ve bunu basit bir iOS uygulamasıyla bağladığımda çok temiz hissettirdi. Sunucu tarafı sadece veri döndürüyor; nasıl gösterileceğini bilmek zorunda değil. İstemci tarafı sadece veriyi alıp gösteriyor; nasıl üretildiğini bilmek zorunda değil.

Bu ayrışma, uygulamayı büyütürken çok işe yarıyor. Web arayüzü, mobil uygulama ve başka bir servis aynı API’yi kullanıyor. Mantığı bir kez yazıyorsunuz.

Etiketler: #Laravel#API
Paylaş:

İlgili Yazılar

Sitede Ara

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

Esc ile kapat Pagefind ile güçlendirildi