home / blog / RESTful API Tasarımı: Gerçekten Önemli K...
Yazılım Mühendisliği Feb 08, 2026

RESTful API Tasarımı: Gerçekten Önemli Kararlar

Çoğul mu tekil mi tartışmasını bırakın. İşte geliştirici deneyimini gerçekten etkileyen API tasarım kararları — versiyonlama, sayfalama, hata formatları ve filtreleme.

RESTful API Tasarımı: Gerçekten Önemli Kararlar

API Tasarimi Gelisitiriciler Icin UX'tir

API'nizin tuketicileri gelistiricilerdir. Her tasarim karari onlarin deneyimini etkiler: ne kadar hizli entegre olabileceklerini, ne siklikla hatalarla karsilasacaklarini, kac destek talebi acacacaklarini. Iyi tasarlanmis bir API, azaltilmis destek yuku ve daha hizli benimseme ile kendini odetir.

Gereksiz tartismalar yerine, gercekten onemli olan kararlara odaklanalim.

Tutarli Hata Yanitlari

Bu, en etkili API tasarim kararidir. Bir seyler ters gittiginde, gelistiricinin bilmesi gerekenler: ne basarisiz oldu, neden basarisiz oldu ve nasil duzeltilir.

// Bad: inconsistent error formats
{ "error": "Not found" }                           // string
{ "errors": { "email": "is required" } }            // object
{ "message": "Server error", "code": 500 }          // different shape
{ "success": false }                                // boolean with no info

// Good: one consistent format for ALL errors
{
    "error": {
        "code": "VALIDATION_ERROR",
        "message": "The given data was invalid.",
        "details": [
            {
                "field": "email",
                "message": "The email field is required."
            },
            {
                "field": "password",
                "message": "The password must be at least 8 characters."
            }
        ]
    }
}

// 404
{
    "error": {
        "code": "RESOURCE_NOT_FOUND",
        "message": "The requested invoice was not found.",
        "details": []
    }
}

// 500
{
    "error": {
        "code": "INTERNAL_ERROR",
        "message": "An unexpected error occurred. Please try again.",
        "details": []
    }
}

Laravel'de bunu bir exception handler ile standartlastirebilirsiniz:

// app/Exceptions/Handler.php
class Handler extends ExceptionHandler
{
    public function render($request, Throwable $e)
    {
        if ($request->expectsJson()) {
            return $this->renderApiError($e);
        }

        return parent::render($request, $e);
    }

    private function renderApiError(Throwable $e): JsonResponse
    {
        return match(true) {
            $e instanceof ValidationException => response()->json([
                'error' => [
                    'code' => 'VALIDATION_ERROR',
                    'message' => $e->getMessage(),
                    'details' => collect($e->errors())->map(fn ($messages, $field) => [
                        'field' => $field,
                        'message' => $messages[0],
                    ])->values(),
                ],
            ], 422),

            $e instanceof ModelNotFoundException => response()->json([
                'error' => [
                    'code' => 'RESOURCE_NOT_FOUND',
                    'message' => 'The requested resource was not found.',
                    'details' => [],
                ],
            ], 404),

            default => response()->json([
                'error' => [
                    'code' => 'INTERNAL_ERROR',
                    'message' => app()->isProduction()
                        ? 'An unexpected error occurred.'
                        : $e->getMessage(),
                    'details' => [],
                ],
            ], 500),
        };
    }
}

Sayfalama: Cursor ve Offset Karsilastirmasi

Offset sayfalama asina olunan bir yontemdir ancak buyuk veri kumelerinde bozulur (ogeler eklendikce/silindikce sayfa kaymasi yasanir). Cursor sayfalama daha guvenilirdir:

// Offset: simple but fragile
GET /api/posts?page=5&per_page=20

// Response includes pagination meta
{
    "data": [...],
    "meta": {
        "current_page": 5,
        "per_page": 20,
        "total": 1847,
        "last_page": 93
    },
    "links": {
        "next": "/api/posts?page=6&per_page=20",
        "prev": "/api/posts?page=4&per_page=20"
    }
}

// Cursor: consistent for real-time data
GET /api/posts?cursor=eyJpZCI6MTAwfQ&limit=20

// Laravel supports this natively
$posts = Post::orderBy('id')->cursorPaginate(20);
return PostResource::collection($posts);

Filtreleme ve Siralama

Olceklenebilir ve pragmatik bir yaklasim:

// Filtering with query parameters
GET /api/orders?status=paid&customer_id=42&created_after=2026-01-01

// Sorting
GET /api/orders?sort=-created_at,total
// Prefix with - for descending

// In Laravel, a simple filter pipeline
class OrderController extends Controller
{
    public function index(Request $request)
    {
        $query = Order::query();

        $query->when($request->status, fn ($q, $status) =>
            $q->where('status', $status)
        );

        $query->when($request->customer_id, fn ($q, $id) =>
            $q->where('customer_id', $id)
        );

        $query->when($request->created_after, fn ($q, $date) =>
            $q->where('created_at', '>=', $date)
        );

        $query->when($request->sort, function ($q, $sort) {
            foreach (explode(',', $sort) as $field) {
                $direction = str_starts_with($field, '-') ? 'desc' : 'asc';
                $q->orderBy(ltrim($field, '-'), $direction);
            }
        });

        return OrderResource::collection($query->cursorPaginate(20));
    }
}

Versiyonlama: Basit Tutun

URL tabanli versiyonlama en acik ve anlasilmasi en kolay yontemdir:

// URL prefix (recommended for most teams)
GET /api/v1/users
GET /api/v2/users

// In Laravel routes
Route::prefix('api/v1')->group(function () {
    Route::apiResource('users', V1\UserController::class);
});

Route::prefix('api/v2')->group(function () {
    Route::apiResource('users', V2\UserController::class);
});

API Resource Tasarim Ipuclari

  • Koleksiyonlar icin cogul isimler kullanin: /api/users, /api/user degil
  • Sahiplik acik oldugunda ic ice yapilandirin: /api/users/42/orders ama 3 veya daha fazla seviye derine inmeyin
  • HTTP metodlarini dogru kullanin: GET okur, POST olusturur, PUT/PATCH gunceller, DELETE siler
  • Olusturma icin 201 donun ve bir Location header'i ekleyin
  • Silme icin 204 donun (icerik yok)
  • Olusturulan/guncellenen kaynagi yanita dahil edin boylece istemci ek bir istek yapmak zorunda kalmaz
  • Yanitlari her zaman bir data anahtariyla sarin: { "data": { ... } } — bu, metadata icin yer birakir

En iyi API dokumantasyonu, dokumantasyona ihtiyac duymayan bir API'dir. Acik adlandirma, tutarli kaliplar ve yardimci hata mesajlari kullanin. Dokumantasyonu karmasik kisimlar icin yazin, acik olanlari icin degil.

tum yazilara don