Zum Hauptinhalt springen

Was sind API-Ressourcen?

Beim Aufbau einer API führt das direkte Ausliefern von Eloquent-Modellen als JSON schnell dazu, dass sensible Spalten mitversendet werden oder unnötig viele Daten an den Client gehen. Eloquent-API-Ressourcen bilden eine Transformationsschicht zwischen Model und JSON-Antwort. In der Methode toArray() legen Sie explizit fest, welche Informationen in welcher Form in die Antwort einfließen. Die Vorteile im Überblick:
  • Volle Kontrolle darüber, welche Felder in der Antwort erscheinen
  • Feldumbenennung und Werttransformation an einem Ort gebündelt
  • Felder lassen sich bedingt ein- oder ausblenden
  • Relationen können verschachtelt werden, um eine konsistente Struktur zu wahren

Ressource erstellen

Erzeugen Sie eine Resource-Klasse mit dem Artisan-Befehl make:resource.
php artisan make:resource UserResource
Die generierte Klasse liegt im Verzeichnis app/Http/Resources.
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}
Mit $this greifen Sie direkt auf die Modelleigenschaften zu. Das ist möglich, weil die Resource-Klasse Zugriffe intern an das Model weiterleitet.

Verwendung im Controller

Definierte Resources können aus einem Controller oder einer Route zurückgegeben werden.
use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/users/{user}', function (User $user) {
    return new UserResource($user);
});
Alternativ verwenden Sie die Methode toResource() des Modells.
Route::get('/users/{user}', function (User $user) {
    return $user->toResource();
});
toResource() sucht anhand des Modellnamens automatisch die passende Resource-Klasse (UserResource). Standardmäßig wird die Antwort in einen Schlüssel data verpackt.
{
  "data": {
    "id": 1,
    "name": "Yamada Taro",
    "email": "[email protected]",
    "created_at": "2024-01-15T10:00:00.000000Z",
    "updated_at": "2024-01-15T10:00:00.000000Z"
  }
}

Resource-Collections

Für mehrere Modelle nutzen Sie die Methode collection().
use App\Http\Resources\UserResource;
use App\Models\User;

Route::get('/users', function () {
    return UserResource::collection(User::all());
});
Alternativ verwenden Sie toResourceCollection() auf einer Eloquent-Collection.
return User::all()->toResourceCollection();

Eigene Collection-Resource

Um Metadaten auf Ebene der gesamten Collection hinzuzufügen, legen Sie eine eigene Collection-Resource an.
php artisan make:resource UserCollection
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;

class UserCollection extends ResourceCollection
{
    public function toArray(Request $request): array
    {
        return [
            'data' => $this->collection,
            'links' => [
                'self' => route('users.index'),
            ],
        ];
    }
}
Route::get('/users', function () {
    return new UserCollection(User::all());
});

Felder transformieren und anpassen

Innerhalb von toArray() können Sie Feldnamen ändern und Werte transformieren.
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'full_name' => $this->name,                         // Feldname geändert
        'email_address' => $this->email,                    // Feldname geändert
        'role' => strtoupper($this->role),                  // Wert transformiert
        'registered_at' => $this->created_at->toDateString(), // Datum formatiert
    ];
}

Bedingte Felder

when() – Feld unter Bedingung hinzufügen

Um ein Feld nur unter bestimmten Voraussetzungen einzublenden, nutzen Sie when().
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        // Nur einbinden, wenn der authentifizierte Nutzer Administrator ist
        'secret_token' => $this->when(
            $request->user()?->isAdmin(),
            $this->secret_token
        ),
    ];
}
Ist die Bedingung von when() false, wird der Schlüssel komplett aus der Antwort entfernt.

mergeWhen() – mehrere Felder gemeinsam bedingen

Sollen mehrere Felder gemeinsam ein- oder ausgeblendet werden, verwenden Sie mergeWhen().
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        $this->mergeWhen($request->user()?->isAdmin(), [
            'admin_note' => $this->admin_note,
            'internal_id' => $this->internal_id,
        ]),
    ];
}

whenLoaded() – nur geladene Relationen einbinden

Indem Sie Relationen nur bei bereits geladenem Eager Loading einbinden, vermeiden Sie das N+1-Problem und behalten die Struktur flexibel.
use App\Http\Resources\PostResource;

public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        // Nur einbinden, wenn posts mit with() geladen wurden
        'posts' => PostResource::collection($this->whenLoaded('posts')),
    ];
}
Ob die Relation geladen wird, steuert der Controller.
// Antwort inklusive posts
return new UserResource($user->load('posts'));

// Antwort ohne posts
return new UserResource($user);

whenCounted() – Zähler bedingt einbinden

Anzahlen, die per loadCount() geladen wurden, lassen sich mit whenCounted() bedingt ausgeben.
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'posts_count' => $this->whenCounted('posts'),
    ];
}
return new UserResource($user->loadCount('posts'));

Verschachtelte Ressourcen

Verschachteln Sie Relationen über eigene Resource-Klassen, um eine konsistente Struktur zu erzielen.
// app/Http/Resources/PostResource.php
class PostResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'author' => new UserResource($this->whenLoaded('user')),
            'comments' => CommentResource::collection($this->whenLoaded('comments')),
            'published_at' => $this->published_at?->toDateString(),
        ];
    }
}
// Controller
$post = Post::with(['user', 'comments'])->findOrFail($id);

return new PostResource($post);
{
  "data": {
    "id": 1,
    "title": "API-Ressourcen in Laravel",
    "author": {
      "id": 5,
      "name": "Yamada Taro",
      "email": "[email protected]"
    },
    "comments": [
      { "id": 10, "body": "Sehr hilfreich!" }
    ]
  }
}

Metadaten hinzufügen

with() – Metadaten auf oberster Ebene

Um Metadaten für die gesamte Collection anzuhängen, überschreiben Sie die Methode with().
class UserCollection extends ResourceCollection
{
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }

    public function with(Request $request): array
    {
        return [
            'meta' => [
                'version' => '1.0',
                'generated_at' => now()->toIso8601String(),
            ],
        ];
    }
}
Beispielhafte Antwort:
{
  "data": [...],
  "meta": {
    "version": "1.0",
    "generated_at": "2024-01-15T10:00:00+00:00"
  }
}

additional() – Metadaten dynamisch ergänzen

Möchten Sie Metadaten dynamisch aus dem Controller heraus hinzufügen, verwenden Sie additional().
return User::all()
    ->load('roles')
    ->toResourceCollection()
    ->additional(['meta' => [
        'total_admins' => User::where('role', 'admin')->count(),
    ]]);

Zusammenspiel mit Paginierung

Wenn Sie ein Paginierungs-Ergebnis an eine Resource übergeben, werden meta und links automatisch ergänzt.
Route::get('/users', function () {
    return UserResource::collection(User::paginate(15));
});
Oder:
return User::paginate(15)->toResourceCollection();
Beispielhafte Antwort:
{
  "data": [
    { "id": 1, "name": "Yamada Taro" },
    { "id": 2, "name": "Suzuki Hanako" }
  ],
  "links": {
    "first": "https://example.com/users?page=1",
    "last": "https://example.com/users?page=5",
    "prev": null,
    "next": "https://example.com/users?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "per_page": 15,
    "to": 15,
    "total": 72
  }
}
Bei Paginierungs-Antworten wird der Schlüssel data immer beibehalten – auch wenn Sie withoutWrapping() aufgerufen haben. Nur so lassen sich die Schlüssel meta und links sauber daneben ausliefern.

Daten-Wrapping deaktivieren

Standardmäßig wird die äußerste Resource in einen Schlüssel data verpackt. Deaktivieren Sie dieses Verhalten mit withoutWrapping() in der boot()-Methode Ihres AppServiceProvider.
use Illuminate\Http\Resources\Json\JsonResource;

public function boot(): void
{
    JsonResource::withoutWrapping();
}
withoutWrapping() wirkt sich nur auf das äußerste Wrapping aus. Ein von Ihnen selbst definierter data-Schlüssel bleibt erhalten.

Praxisbeispiel: eine Nutzer-API implementieren

Am Beispiel einer Nutzerverwaltungs-API zeigen wir, wie sich konsistente Antworten mit Ressourcen umsetzen lassen.

UserResource

<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'avatar_url' => $this->avatar_url,
            'role' => $this->role,
            // Nur für Administratoren sichtbar
            'created_at' => $this->when(
                $request->user()?->isAdmin(),
                $this->created_at->toDateString()
            ),
            // Nur einbinden, wenn geladen
            'posts' => PostResource::collection($this->whenLoaded('posts')),
            'posts_count' => $this->whenCounted('posts'),
        ];
    }
}

UserController

<?php

namespace App\Http\Controllers\Api;

use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class UserController extends Controller
{
    public function index(): AnonymousResourceCollection
    {
        $users = User::withCount('posts')->paginate(20);

        return UserResource::collection($users);
    }

    public function show(User $user): UserResource
    {
        $user->loadCount('posts')->load('posts');

        return new UserResource($user);
    }
}

Verwandte Seiten

Einführung in Eloquent-Relationen

Auffrischung zur Definition von Relationen und zu Eager Loading.

Paginierung

Erfahren Sie, wie Sie Paginierungs-Ergebnisse mit API-Ressourcen kombinieren.
Zuletzt geändert am 13. Juli 2026