slogan3

Ontwikkel een Laravel API met GraphQL Integratie en Geavanceerde Query Optimalisatie

description

Met deze prompt kan de gebruiker een moderne, flexibele API ontwerpen die complexe data queries efficiënt verwerkt via GraphQL. Het helpt bij het verbeteren van prestaties door geavanceerde query optimalisatie en zorgt voor een veilige implementatie met gespecificeerde authenticatie- en autorisatiemethoden. Dit is een waardevolle aanvulling naast REST API’s en traditionele Laravel oplossingen.

prompt

Help me een Laravel API te bouwen die GraphQL gebruikt voor flexibele dataquery’s en -mutaties. Ik wil dat je uitlegt hoe ik een GraphQL schema kan definiëren voor <specifieke data-entiteiten>, en hoe ik resolvers kan implementeren die efficiënt om ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een uitgebreide gids voor het bouwen van een Laravel API die GraphQL gebruikt, inclusief schema-definitie, resolvers, optimalisatie, batching, en beveiliging met JWT en scopes. --- ### 1. Setup van Laravel met GraphQL **Stap 1:** Installeer benodigde pakketten: - GraphQL Laravel Package: bijvoorbeeld `rebing/graphql-laravel` - JWT Authenticatie: bijvoorbeeld `tymon/jwt-auth` ```bash composer require rebing/graphql-laravel composer require tymon/jwt-auth ``` **Stap 2:** Publiceer configuraties en migraties: ```bash php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider" php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" ``` **Stap 3:** Configureer JWT in `config/jwt.php` en voeg middleware toe. --- ### 2. GraphQL Schema Definiëren **a. Types voor Product, Bestelling, Klant** ```php // app/GraphQL/Types/ProductType.php namespace App\GraphQL\Types; use GraphQL\Type\Definition\Type; use Rebing\GraphQL\Support\Type as GraphQLType; class ProductType extends GraphQLType { protected $attributes = [ 'name' => 'Product', 'description' => 'Een product', ]; public function fields(): array { return [ 'id' => [ 'type' => Type::nonNull(Type::int()), ], 'naam' => [ 'type' => Type::nonNull(Type::string()), ], 'prijs' => [ 'type' => Type::nonNull(Type::float()), ], 'categorie' => [ 'type' => Type::string(), ], ]; } } ``` Similarly voor Bestelling en Klant. **b. Query Types** ```php // app/GraphQL/Queries/ProductQuery.php namespace App\GraphQL\Queries; use GraphQL\Type\Definition\Type; use Rebing\GraphQL\Support\Query; use App\Models\Product; class ProductQuery extends Query { protected $attributes = [ 'name' => 'products', ]; public function type(): Type { return Type::listOf(GraphQL::type('Product')); } public function args(): array { return [ 'id' => [ 'name' => 'id', 'type' => Type::int(), ], 'naam' => [ 'name' => 'naam', 'type' => Type::string(), ], // voeg filters toe ]; } public function resolve($root, $args) { $query = Product::query(); if (isset($args['id'])) { $query->where('id', $args['id']); } if (isset($args['naam'])) { $query->where('naam', 'like', '%' . $args['naam'] . '%'); } return $query->get(); } } ``` Herhaal voor bestellingen en klanten. --- ### 3. Resolvers voor complexe queries en optimalisatie **a. Eager Loading voor N+1 Probleem** Gebruik `with()` om gerelateerde data in één query op te halen: ```php public function resolve($root, $args) { $query = Product::with('categorie'); // filters return $query->get(); } ``` **b. Batching en DataLoader** Gebruik DataLoader (bijvoorbeeld met `rebing/graphql-laravel` of `dataloader-php`) om meerdere gerelateerde gegevens in één batch te laden. Dit vermindert het aantal database queries bij complexe nested queries. --- ### 4. Query-optimalisatie technieken - **Eager Loading**: voorkom N+1 problemen. - **Indexes**: Zorg voor indexen op veelgebruikte zoekvelden. - **Limit & Offset**: voor grote datasets, voeg paginatie toe. - **Complexiteit beperken**: gebruik `GraphQL::setMaxQueryComplexity()` om brute force te voorkomen. - **Caching**: cache resultaten van veelgevraagde data. --- ### 5. Batching en DataLoader Gebruik bijvoorbeeld `rebing/graphql-laravel`'s ingebouwde batching of integreer `dataloader-php`: ```php // Implementatie van DataLoader voor gerelateerde data // bijvoorbeeld om bestellingen voor klanten te batchen ``` Dit vermindert het aantal database queries bij nested queries. --- ### 6. Beveiliging: JWT tokens met scopes voor toegang **a. JWT Authenticatie** - Voeg middleware toe aan GraphQL routes: ```php // in routes/graphql.php Route::group(['middleware' => ['auth:api']], function() { // graphql endpoint }); ``` - In `app/Http/Kernel.php`, zorg dat 'auth:api' JWT gebruikt. **b. Scopes en autorisatie** Gebruik scopes binnen JWT tokens om verschillende rechten te definiëren: ```php // Aanmaken token met scopes $token = JWTAuth::fromUser($user, ['scopes' => ['producten:lees', 'bestellingen:schrijven']]); ``` **c. Verifiëren van scopes in resolvers of middleware** Maak een middleware die controleert of de JWT token de juiste scope bevat: ```php // app/Http/Middleware/CheckScope.php public function handle($request, Closure $next, $scope) { $user = JWTAuth::parseToken()->authenticate(); $scopes = JWTAuth::getPayload()->get('scopes'); if (!in_array($scope, $scopes)) { return response()->json(['error' => 'Onvoldoende rechten'], 403); } return $next($request); } ``` En voeg deze middleware toe aan je route of resolver. --- ### Samengevat - Definieer types en queries in GraphQL schema - Gebruik eager loading en batching voor performantie - Pas query complexiteit en paginatie toe - Beveilig endpoints met JWT tokens en scopes - Controleer scopes binnen resolvers of middleware Wil je dat ik je help met concrete codevoorbeelden voor een specifiek onderdeel of meer uitleg over een bepaald aspect?
provider: deepseek
model: deepseek-chat
Hallo! Ik help je graag met het bouwen van een Laravel API met GraphQL. Hier is een complete uitleg in het Nederlands: ## 1. Vereisten installeren ```bash composer require rebing/graphql-laravel php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider" ``` ## 2. GraphQL Schema definiëren **Product Type:** ```php // graphql/types/ProductType.php namespace App\GraphQL\Types; use App\Models\Product; use GraphQL\Type\Definition\Type; use Rebing\GraphQL\Support\Type as GraphQLType; class ProductType extends GraphQLType { protected $attributes = [ 'name' => 'Product', 'description' => 'Een product', 'model' => Product::class ]; public function fields(): array { return [ 'id' => [ 'type' => Type::nonNull(Type::int()), 'description' => 'ID van het product', ], 'name' => [ 'type' => Type::nonNull(Type::string()), 'description' => 'Naam van het product', ], 'price' => [ 'type' => Type::nonNull(Type::float()), 'description' => 'Prijs van het product', ], 'orders' => [ 'type' => Type::listOf(\GraphQL::type('Order')), 'description' => 'Bestellingen met dit product', ] ]; } } ``` **Query Class:** ```php // graphql/queries/ProductsQuery.php namespace App\GraphQL\Queries; use App\Models\Product; use GraphQL\Type\Definition\Type; use Rebing\GraphQL\Support\Query; use GraphQL\Type\Definition\ResolveInfo; use Rebing\GraphQL\Support\Facades\GraphQL; class ProductsQuery extends Query { protected $attributes = [ 'name' => 'products', ]; public function type(): Type { return Type::listOf(GraphQL::type('Product')); } public function args(): array { return [ 'id' => ['name' => 'id', 'type' => Type::int()], 'name' => ['name' => 'name', 'type' => Type::string()], ]; } public function resolve($root, $args, $context, ResolveInfo $resolveInfo) { $query = Product::query(); if (isset($args['id'])) { $query->where('id', $args['id']); } if (isset($args['name'])) { $query->where('name', 'like', "%{$args['name']}%"); } return $query->get(); } } ``` ## 3. Resolvers implementeren met eager loading ```php // graphql/types/OrderType.php public function fields(): array { return [ 'id' => Type::nonNull(Type::int()), 'customer' => [ 'type' => \GraphQL::type('Customer'), 'resolve' => function($order, $args, $context, ResolveInfo $info) { // Eager loading om N+1 problemen te voorkomen return $order->customer; } ], 'products' => [ 'type' => Type::listOf(\GraphQL::type('Product')), 'resolve' => function($order, $args, $context, $info) { return $order->products()->with('category')->get(); } ] ]; } ``` ## 4. Query optimalisatie en batching **DataLoader voor batching:** ```php // app/GraphQL/DataLoaders/ProductDataLoader.php namespace App\GraphQL\DataLoaders; use App\Models\Product; use Illuminate\Support\Facades\DB; use GraphQL\Deferred; class ProductDataLoader { public static function batchLoad(array $keys) { $products = Product::whereIn('id', $keys)->get()->keyBy('id'); return array_map(function($key) use ($products) { return new Deferred(function() use ($products, $key) { return $products[$key] ?? null; }); }, $keys); } } ``` **Gebruik in resolver:** ```php public function resolve($root, $args, $context, ResolveInfo $info) { $fieldName = $info->fieldName; if ($fieldName === 'products') { return ProductDataLoader::batchLoad([$root->id]); } return $root->$fieldName; } ``` ## 5. Beveiliging met JWT en scopes **JWT Authenticatie installeren:** ```bash composer require tymon/jwt-auth php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" php artisan jwt:secret ``` **Middleware voor GraphQL:** ```php // app/Http/Middleware/GraphQLAuth.php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; use Tymon\JWTAuth\Facades\JWTAuth; class GraphQLAuth { public function handle(Request $request, Closure $next): Response { try { $token = $request->bearerToken(); if (!$token) { return response()->json(['error' => 'Token vereist'], 401); } JWTAuth::setToken($token)->authenticate(); } catch (\Exception $e) { return response()->json(['error' => 'Ongeldig token'], 401); } return $next($request); } } ``` **Scope-based autorisatie:** ```php // app/GraphQL/Middleware/ScopeMiddleware.php namespace App\GraphQL\Middleware; use Closure; use GraphQL\Type\Definition\ResolveInfo; use Rebing\GraphQL\Support\Middleware; use Tymon\JWTAuth\Facades\JWTAuth; class ScopeMiddleware extends Middleware { public function handle($root, $args, $context, ResolveInfo $info, Closure $next) { $requiredScopes = $this->getRequiredScopes($info->fieldName); if (!$this->hasScopes($requiredScopes)) { throw new \Exception('Onvoldoende rechten'); } return $next($root, $args, $context, $info); } private function getRequiredScopes(string $field): array { $scopes = [ 'createProduct' => ['product:write'], 'updateProduct' => ['product:write'], 'deleteProduct' => ['product:write'], 'products' => ['product:read'], 'orders' => ['order:read'], ]; return $scopes[$field] ?? []; } private function hasScopes(array $requiredScopes): bool { $userScopes = JWTAuth::getPayload()->get('scopes') ?? []; return empty($requiredScopes) || !empty(array_intersect($requiredScopes, $userScopes)); } } ``` ## 6. GraphQL configuratie ```php // config/graphql.php 'schemas' => [ 'default' => [ 'query' => [ 'products' => App\GraphQL\Queries\ProductsQuery::class, 'orders' => App\GraphQL\Queries\OrdersQuery::class, ], 'mutation' => [ 'createProduct' => App\GraphQL\Mutations\CreateProductMutation::class, ], 'middleware' => [ 'auth:api', App\GraphQL\Middleware\ScopeMiddleware::class, ], ], ], ``` ## 7. Query depth limiting en complexity analysis ```php // config/graphql.php 'security' => [ 'query_max_complexity' => 1000, 'query_max_depth' => 15, 'disable_introspection' => env('GRAPHQL_DISABLE_INTROSPECTION', false), ], ``` ## 8. Gebruik in frontend ```graphql query { products { id name price orders { id customer { name email } } } } ``` ## Belangrijke tips: 1. **Eager loading**: Gebruik `with()` om N+1 problemen te voorkomen 2. **Pagination**: Implementeer cursor-based pagination voor grote datasets 3. **Caching**: Gebruik Redis of memcached voor query caching 4. **Monitoring**: Monitor query performance met Laravel Telescope 5. **Validation**: Valideer alle input in mutations Dit geeft je een solide basis voor een veilige en efficiënte GraphQL API in Laravel!