Erstellen eines GraphQL-Servers mit Laravel
Veröffentlicht: 2022-03-11Falls Sie noch nicht damit vertraut sind, GraphQL ist eine Abfragesprache, die zur Interaktion mit Ihrer API verwendet wird und einige Vorteile im Vergleich zu alternativen Architekturen wie REST bietet. GraphQL ist äußerst praktisch, wenn es als Endpunkt für mobile und Single-Page-Anwendungen verwendet wird. Mit GraphQL können Sie relativ einfach verschachtelte und verwandte Daten in einer Anfrage abfragen, sodass Entwickler die genauen Daten, die sie benötigen, in einem einzigen Roundtrip zum Server abrufen können.
Laravel ist ein beliebtes, rechthaberisches PHP-Webframework. Es bietet zahlreiche integrierte Tools, um Anwendungen schnell zum Laufen zu bringen, aber es ermöglicht Entwicklern auch, ihre eigenen Implementierungen gegen die integrierten Schnittstellen von Laravel auszutauschen, wenn dies bevorzugt wird.
Obwohl die Gemeinschaften, die sowohl GraphQL als auch Laravel umgeben, dramatisch gewachsen sind, seit sie Open Source sind, ist die Dokumentation, die erklärt, wie man diese beiden Technologien zusammen verwendet, immer noch etwas spärlich.
In diesem Tutorial zeige ich Ihnen also, wie Sie mit Laravel Ihren eigenen GraphQL-Server erstellen.
Projektübersicht
Bevor wir beginnen, müssen wir uns mit dem Projekt vertraut machen, das wir zu erstellen versuchen. Dazu definieren wir unsere Ressourcen und erstellen unser GraphQL-Schema, das wir später verwenden werden, um unsere API bereitzustellen.
Projektressourcen
Unsere Anwendung besteht aus zwei Ressourcen: Artikel und Benutzer . Diese Ressourcen werden in unserem GraphQL-Schema als Objekttypen definiert:
type User { id: ID! name: String! email: String! articles: [Article!]! } type Article { id: ID! title: String! content: String! author: User! }
Wenn wir uns das Schema ansehen, können wir sehen, dass wir eine Eins-zu-Viele-Beziehung zwischen unseren beiden Objekten haben. Benutzer können viele Artikel schreiben, und einem Artikel ist ein Autor (Benutzer) zugeordnet.
Nachdem wir nun unsere Objekttypen definiert haben, brauchen wir eine Möglichkeit, unsere Daten zu erstellen und abzufragen, also definieren wir unsere Abfrage- und Mutationsobjekte:
type Query { user(id: ID!): User users: [User!]! article(id: ID!): Article articles: [Article!]! } type Mutation { createUser(name: String!, email: String!, password: String!): User createArticle(title: String!, content: String!): Article }
Einrichtung unseres Laravel-Projekts
Nachdem wir nun unser GraphQL-Schema definiert haben, lassen Sie uns unser Laravel-Projekt zum Laufen bringen. Beginnen wir damit, ein neues Laravel via Composer-Projekt zu erstellen:
$ composer create-project --prefer-dist laravel/laravel laravel-graphql
Um sicherzustellen, dass alles funktioniert, starten wir unseren Server und stellen sicher, dass wir die Standardseite von Laravel sehen:
$ cd laravel-graphql $ php artisan serve Laravel development server started: <http://127.0.0.1:8000>
Datenbankmodelle und Migrationen
Für die Zwecke dieses Artikels verwenden wir SQLite. Nehmen wir also die folgenden Änderungen an der standardmäßigen .env
-Datei vor:
DB_CONNECTION=sqlite # DB_HOST= # DB_PORT= # DB_DATABASE=database.sqlite # DB_USERNAME= # DB_PASSWORD=
Als nächstes erstellen wir unsere Datenbankdatei:
$ touch ./database/database.sqlite
Laravel wird mit einem Benutzermodell und einigen grundlegenden Migrationsdateien ausgeliefert. Lassen Sie uns schnell eine api_token
-Spalte zu unserer von Laravel bereitgestellten CreateUsersTable
Migrationsdatei hinzufügen:
/database/migrations/XXXX_XX_XX_000000_create_users_table.php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateUsersTable extends Migration { /** * Run the migrations. */ public function up() { Schema::create('users', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->string('api_token', 80)->unique()->nullable()->default(null); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down() { Schema::dropIfExists('users'); } }
Wir werden später in diesem Artikel auf diese zusätzliche Spalte zurückkommen, wenn wir zur Autorisierung kommen. Lassen Sie uns nun fortfahren und unser Artikelmodell und eine Migrationsdatei erstellen, um die zugehörige Tabelle zu erstellen:
$ php artisan make:model Article -m
Hinweis: Die Option -m erstellt eine Migrationsdatei für unser neu erstelltes Artikelmodell.
Nehmen wir einige Anpassungen an der generierten Migrationsdatei vor:
use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateArticlesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('articles', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('user_id'); $table->string('title'); $table->text('content'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('articles'); } }
Wir haben einen Fremdschlüssel hinzugefügt, der auf die id
in unserer users
sowie die title
und content
verweist, die wir in unserem GraphQL-Schema definiert haben.
Nachdem wir nun unsere Migrationsdateien definiert haben, lassen Sie uns fortfahren und sie gegen unsere Datenbank ausführen:
$ php artisan migrate
Als Nächstes aktualisieren wir unsere Modelle, indem wir die erforderlichen Beziehungen definieren:
app/User.php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; // ... /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function articles() { return $this->hasMany(Article::class); } }
app/Article.php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'title', 'content', ]; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function user() { return $this->belongsTo(User::class); } }
Datenbank-Seeding
Nachdem wir nun unsere Modelle und Migrationen eingerichtet haben, lassen Sie uns unsere Datenbank mit einem Seed versehen. Wir beginnen damit, einige Seeder-Klassen für unsere articles
und users
zu erstellen:
$ php artisan make:seeder UsersTableSeeder $ php artisan make:seeder ArticlesTableSeeder
Als Nächstes richten wir sie so ein, dass sie einige Dummy-Daten in unsere SQLite-Datenbank einfügen:
database/seeds/UsersTableSeeder.php use App\User; use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { /** * Run the database seeds. */ public function run() { \App\User::truncate(); $faker = \Faker\Factory::create(); $password = bcrypt('secret'); \App\User::create([ 'name' => $faker->name, 'email' => '[email protected]', 'password' => $password, ]); for ($i = 0; $i < 10; ++$i) { \App\User::create([ 'name' => $faker->name, 'email' => $faker->email, 'password' => $password, ]); } } }
database/seeds/ArticlesTableSeeder.php use App\Article; use Illuminate\Database\Seeder; class ArticlesTableSeeder extends Seeder { /** * Run the database seeds. */ public function run() { \App\Article::truncate(); \App\Article::unguard(); $faker = \Faker\Factory::create(); \App\User::all()->each(function ($user) use ($faker) { foreach (range(1, 5) as $i) { \App\Article::create([ 'user_id' => $user->id, 'title' => $faker->sentence, 'content' => $faker->paragraphs(3, true), ]); } }); } }
/database/seeds/DatabaseSeeder.php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(UsersTableSeeder::class); $this->call(ArticlesTableSeeder::class); } }
Lassen Sie uns zum Schluss unsere Datenbank-Seeder ausführen, um einige Daten in unsere Datenbank zu bekommen:
$ php artisan db:seed
Laravel Lighthouse und GraphQL-Server
Nachdem wir unsere Datenbank und Modelle eingerichtet haben, ist es an der Zeit, mit dem Aufbau unseres GraphQL-Servers zu beginnen. Derzeit sind mehrere Lösungen für Laravel verfügbar, aber für diesen Artikel werden wir Lighthouse verwenden.
Lighthouse ist ein Paket, das ich vor ein paar Jahren erstellt habe und das kürzlich von der wachsenden Community um es herum erstaunlich unterstützt wurde. Es ermöglicht Entwicklern die schnelle Einrichtung eines GraphQL-Servers mit Laravel mit wenig Boilerplate und ist gleichzeitig flexibel genug, um es Entwicklern zu ermöglichen, ihn an die Anforderungen nahezu jedes Projekts anzupassen.
Beginnen wir damit, das Paket in unser Projekt zu ziehen:
$ composer require nuwave/lighthouse:"3.1.*"
Als Nächstes veröffentlichen wir die Konfigurationsdatei von Lighthouse:
$ php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
Hinweis: Sie können auch die Standardschemadatei von Lighthouse veröffentlichen, indem Sie einfach die Option --tag=config
entfernen. Aber für die Zwecke dieses Artikels werden wir unsere Schemadatei von Grund auf neu erstellen.
Wenn wir uns die Datei config/lighthouse.php
ansehen, werden Sie eine Einstellung bemerken, die verwendet wird, um unsere Schemadatei bei Lighthouse zu registrieren:
'schema' => [ 'register' => base_path('graphql/schema.graphql'), ],
Lassen Sie uns also fortfahren und unsere Schemadatei erstellen und unseren Benutzerobjekttyp und unsere Abfrage einrichten:
$ mkdir graphql $ touch ./graphql/schema.graphql /graphql/schema.graphql type User { id: ID! name: String! email: String! } type Query { user(id: ID! @eq): User @find users: [User!]! @all }
Sie werden feststellen, dass unser Schema dem zuvor definierten ähnelt, außer dass wir einige Bezeichner hinzugefügt haben, die als Schemadirektiven bezeichnet werden.
Nehmen wir uns einen Moment Zeit, um unser definiertes Schema aufzuschlüsseln. Unsere erste Definition ist ein Objekttyp namens User
, der eine Beziehung zu unserem eloquenten App\User
-Modell hat. Wir haben id
, name
und email
als Felder definiert, die von unseren User
abgefragt werden können. Alternativ bedeutet dies, dass die Spalten password
, created_at
und updated_at
Felder sind, die nicht von unserer API abgefragt werden können.
Als nächstes haben wir unseren Query
, der ein Einstiegspunkt in unsere API ist und zum Abfragen von Daten verwendet werden kann. Unser erstes Feld ist das users
, das ein Array von User
zurückgibt. Die Direktive @all
weist Lighthouse an, eine Eloquent-Abfrage unter Verwendung unseres User
auszuführen und alle Ergebnisse abzurufen. Dies wäre dasselbe wie das Ausführen des folgenden:
$users = \App\User::all();
Hinweis: Lighthouse weiß aufgrund der in seiner Konfigurationsdatei definierten namespaces
-Option, dass es im \App\User
-Namespace nach einem Modell suchen muss.
Unser zweites definiertes Feld für unseren Abfragetyp ist der Aufruf user
, der eine id
als Argument akzeptiert und einen einzelnen User
zurückgibt. Wir haben auch zwei Direktiven hinzugefügt, um Lighthouse dabei zu helfen, automatisch eine Abfrage für uns zu erstellen und ein einzelnes User
zurückzugeben. Die @eq
Direktive weist Lighthouse an, ein where in unsere id
-Spalte einzufügen, und die @find
Direktive weist Lighthouse an, ein einzelnes Ergebnis zurückzugeben. Um diese Abfrage mit dem Abfragegenerator von Laravel zu schreiben, würde sie folgendermaßen aussehen:
$user = \App\User::where('id', $args['id'])->first();
Abfragen unserer GraphQL-API
Nachdem wir nun einen kleinen Einblick haben, wie Lighthouse unser Schema zum Erstellen von Abfragen verwendet, lassen Sie uns unseren Server ausführen und mit dem Abfragen von Daten beginnen. Wir beginnen damit, unseren Server auszuführen:
$ php artisan serve Laravel development server started: <http://127.0.0.1:8000>
Um einen GraphQL-Endpunkt abzufragen, können Sie den cURL-Befehl im Terminal oder einem Standard-Client wie Postman ausführen. Um jedoch alle Vorteile von GraphQL nutzen zu können (z. B. automatische Vervollständigung, Fehlerhervorhebung, Dokumentation usw.), verwenden wir GraphQL Playground (Downloads hier).
Klicken Sie beim Starten von Playground auf die Registerkarte „URL-Endpunkt“ und geben Sie http://localhost:8000/graphql ein, um GraphQL Playground auf unseren Server zu verweisen. Auf der linken Seite des Editors können wir unsere Daten abfragen, also fragen wir zunächst nach allen Benutzern, mit denen wir die Datenbank gesät haben:
{ users { id email name } }
Wenn Sie in der Mitte der IDE auf die Wiedergabeschaltfläche klicken (oder auf Strg+Enter klicken), sehen Sie auf der rechten Seite die JSON-Ausgabe unseres Servers, die ungefähr so aussehen wird:
{ "data": { "users": [ { "id": "1", "email": "[email protected]", "name": "Carolyn Powlowski" }, { "id": "2", "email": "[email protected]", "name": "Elouise Raynor" }, { "id": "3", "email": "[email protected]", "name": "Mrs. Dejah Wiza" }, ... ] } }
Hinweis: Da wir Faker zum Seeding unserer Datenbank verwendet haben, werden die Daten in den E- email
und name
unterschiedlich sein.
Lassen Sie uns nun versuchen, einen einzelnen Benutzer abzufragen:
{ user(id: 1) { email name } }
Und wir erhalten die folgende Ausgabe für einen einzelnen Benutzer:
{ "data": { "user": { "email": "[email protected]", "name": "Carolyn Powlowski" } } }
Das Abfragen von Daten wie diesem ist für den Anfang gut, aber es ist höchst unwahrscheinlich, dass Sie sich in einem Projekt befinden, in dem Sie jemals alle Ihre Daten abfragen möchten. Lassen Sie uns also versuchen, eine Paginierung hinzuzufügen. Wenn wir die große Auswahl an integrierten Direktiven von Lighthouse durchsehen, steht uns eine @paginate
-Direktive zur Verfügung, also aktualisieren wir das Abfrageobjekt unseres Schemas wie folgt:
type Query { user(id: ID! @eq): User @find users: [User!]! @paginate }
Wenn wir GraphQL Playground neu laden ( Strg/Cmd + R ) und unsere users
erneut versuchen, werden Sie feststellen, dass wir eine Fehlermeldung erhalten, die besagt, dass das Cannot query field "id" on type "UserPaginator"
werden kann. Was ist also passiert? Hinter den Kulissen manipuliert Lighthouse unser Schema, damit wir einen paginierten Satz von Ergebnissen erhalten, und zwar durch Ändern des Rückgabetyps unseres users
.

Lassen Sie uns einen genaueren Blick darauf werfen, indem wir unser Schema auf der Registerkarte „Dokumente“ von GraphQL Playground untersuchen. Wenn Sie sich das users
ansehen, gibt es einen UserPaginator
zurück, der ein Array von Benutzern und einen von Lighthouse definierten PaginatorInfo
Typ zurückgibt:
type UserPaginator { paginatorInfo: PaginatorInfo! data: [User!]! } type PaginatorInfo { count: Int! currentPage: Int! firstItem: Int hasMorePages: Boolean! lastItem: Int lastPage: Int! perPage: Int! total: Int! }
Wenn Sie mit der integrierten Paginierung von Laravel vertraut sind, werden Ihnen die im PaginatorInfo
Typ verfügbaren Felder wahrscheinlich sehr bekannt vorkommen. Um also zwei Benutzer abzufragen, die Gesamtzahl der Benutzer im System abzurufen und zu überprüfen, ob wir mehr Seiten zum Durchblättern haben, würden wir die folgende Abfrage senden:
{ users(count:2) { paginatorInfo { total hasMorePages } data { id name email } } }
Was uns die folgende Antwort liefern wird:
{ "data": { "users": { "paginatorInfo": { "total": 11, "hasMorePages": true }, "data": [ { "id": "1", "name": "Carolyn Powlowski", "email": "[email protected]" }, { "id": "2", "name": "Elouise Raynor", "email": "[email protected]" }, ] } } }
Beziehungen
Im Allgemeinen werden bei der Entwicklung einer Anwendung viele Ihrer Daten verknüpft. In unserem Fall kann ein User
viele Articles
schreiben, also fügen wir diese Beziehung zu unserem Benutzertyp hinzu und definieren unseren Article
:
type User { id: ID! name: String! email: String! articles: [Article!]! @hasMany } type Article { id: ID! title: String! content: String! }
Hier verwenden wir eine weitere von Lighthouse bereitgestellte Schema-Direktive @hasMany
, die Lighthouse mitteilt, dass unser User
eine \Illuminate\Database\Eloquent\Relations\HasMany
Beziehung mit dem Article
-Modell hat.
Lassen Sie uns nun unsere neu definierte Beziehung abfragen:
{ user(id:1) { articles { id title } } }
Dies liefert uns die folgende Antwort:
{ "data": { "user": { "articles": [ { "id": "1", "title": "Aut velit et temporibus ut et tempora sint." }, { "id": "2", "title": "Voluptatem sed labore ea voluptas." }, { "id": "3", "title": "Beatae sit et maxime consequatur et natus totam." }, { "id": "4", "title": "Corrupti beatae cumque accusamus." }, { "id": "5", "title": "Aperiam quidem sit esse rem sed cupiditate." } ] } } }
Lassen Sie uns schließlich unsere Beziehung umkehren und unsere author
zu unserem Article
hinzufügen, indem wir die @belongsTo
von Lighthouse verwenden und unsere Query
aktualisieren:
type Article { id: ID! title: String! content: String! author: User! @belongsTo(relation: "user") } type Query { user(id: ID! @eq): User @find users: [User!]! @paginate article(id: ID! @eq): Article @find articles: [Article!]! @paginate }
Sie werden sehen, dass wir der Direktive @belongsTo
ein optionales relation
-Argument hinzugefügt haben. Dadurch wird Lighthouse angewiesen, die user
des Articles
-Modells zu verwenden und sie dem author
zuzuweisen.
Lassen Sie uns nun eine Liste von Artikeln abfragen und den zugehörigen Autor abrufen:
{ articles(count:2) { paginatorInfo { total hasMorePages } data { id title author { name email } } } }
Und wir sollten Folgendes von unserem Server erhalten:
{ "data": { "articles": { "paginatorInfo": { "total": 55, "hasMorePages": true }, "data": [ { "id": "1", "title": "Aut velit et temporibus ut et tempora sint.", "author": { "name": "Carolyn Powlowski", "email": "[email protected]" } }, { "id": "2", "title": "Voluptatem sed labore ea voluptas.", "author": { "name": "Carolyn Powlowski", "email": "[email protected]" } } ] } } }
GraphQL-Mutation
Nachdem wir nun unsere Daten abfragen können, erstellen wir einige Mutationen, um einige neue Benutzer und Artikel zu erstellen. Wir beginnen mit unserem Benutzermodell:
type Mutation { createUser( name: String! email: String! @rules(apply: ["email", "unique:users"]) password: String! @bcrypt @rules(apply: ["min:6"]) ): User @create }
Lassen Sie uns nun diese Schemadefinition aufschlüsseln. Wir haben eine Mutation namens createUser
, die drei Argumente akzeptiert ( name
, email
und password
). Wir haben die @rules
Direktive sowohl auf unsere E- email
als auch auf unsere password
angewendet. Dies mag Ihnen ein wenig bekannt vorkommen, da es der Validierungslogik ähnelt, die Laravel für seine Controller bereitstellt.
Als Nächstes haben wir die Direktive @bcrypt
an unser password
angehängt. Dadurch wird das Kennwort verschlüsselt, bevor es an das neu erstellte Modell weitergegeben wird.
Um uns schließlich beim Erstellen neuer Modelle zu helfen, stellt Lighthouse eine @create
Schema-Direktive bereit, die die von uns definierten Argumente übernimmt und ein neues Modell erstellt. Das Ausführen derselben Logik in einem Controller würde wie folgt aussehen:
namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller { /** * Create a new user. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $data = $this->validate($request, [ 'email' => ['email', 'unique:users'], 'password' => ['min:6'] ]); $user = \App\User::create($data); return response()->json(['user' => $user]); } }
Nachdem wir nun unser createUser-Mutationsfeld eingerichtet haben, lassen Sie uns fortfahren und es in GraphQL Playground mit dem Folgenden ausführen:
mutation { createUser( name:"John Doe" email:"[email protected]" password: "secret" ) { id name email } }
Wir sollten die folgende Ausgabe erhalten:
{ "data": { "createUser": { "id": "12", "name": "John Doe", "email": "[email protected]" } } }
GraphQL-Authentifizierung und -Autorisierung
Da wir unseren Article
eine user_id
hinzufügen müssen, wäre jetzt ein guter Zeitpunkt, um die Authentifizierung und Autorisierung in GraphQL/Lighthouse durchzugehen.
Um einen Benutzer zu authentifizieren, müssen wir ihm ein api_token
, also erstellen wir eine Mutation, um dies zu handhaben, und fügen die Direktive @field
hinzu, um Lighthouse auf einen benutzerdefinierten Resolver zu verweisen, der die Logik verarbeitet. Wir setzen den Resolver nach dem gleichen Muster wie das Definieren eines Controllers in Laravel mit dem resolver
-Argument.
Mit der unten definierten @field
Direktive teilen wir Lighthouse mit, wenn die login
-Mutation ausgeführt wird, verwenden Sie die createToken
Methode in unserer App\GraphQL\Mutations\AuthMutator
-Klasse:
type Mutation { # ... login( email: String! password: String! ): String @field(resolver: "AuthMutator@resolve") }
Hinweis: Sie müssen hier nicht den gesamten Namespace angeben. In der Konfigurationsdatei lighthouse.php
sehen Sie, dass der Namespace für unsere Mutationen bereits als App\\GraphQL\\Mutations
definiert ist – Sie können jedoch den vollständigen Namespace verwenden, wenn Sie dies vorziehen.
Verwenden wir den Lighthouse-Generator, um die neue Mutator-Klasse zu erstellen:
$ php artisan lighthouse:mutation AuthMutator
Als nächstes aktualisieren wir unsere Resolver-Funktion wie folgt:
namespace App\GraphQL\Mutations; use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Support\Facades\Auth; use GraphQL\Type\Definition\ResolveInfo; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; class AuthMutator { /** * Return a value for the field. * * @param null $rootValue Usually contains the result returned from the parent field. In this case, it is always `null`. * @param mixed[] $args The arguments that were passed into the field. * @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context Arbitrary data that is shared between all fields of a single query. * @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo Information about the query itself, such as the execution state, the field name, path to the field from the root, and more. * @return mixed */ public function resolve($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) { $credentials = Arr::only($args, ['email', 'password']); if (Auth::once($credentials)) { $token = Str::random(60); $user = auth()->user(); $user->api_token = $token; $user->save(); return $token; } return null; } }
Nachdem wir unseren Resolver eingerichtet haben, testen wir ihn und versuchen, ein API-Token mit der folgenden Mutation in GraphQL Playground zu erhalten:
mutation { login(email:"[email protected]", password:"secret") }
Wir sollten ein Token wie folgt an uns zurückgeschickt bekommen:
{ "data": { "login": "VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE" } }
Hinweis: Achten Sie darauf, das von der Login-Mutation zurückgegebene Token zu kopieren, damit wir es später verwenden können.
Als Nächstes fügen wir ein Abfragefeld hinzu, das den authentifizierten Benutzer zurückgibt, um sicherzustellen, dass unsere Logik funktioniert. Wir fügen ein Feld namens me
hinzu und verwenden die @auth
-Direktive von Lighthouse, um den aktuell authentifizierten Benutzer zurückzugeben. Wir werden auch das guard
Argument gleich api
setzen, da wir so den Benutzer authentifizieren werden.
type Query { # ... me: User @auth(guard: "api") }
Lassen Sie uns nun die Abfrage ausführen. In GraphQL Playground können Sie Ihre Anforderungsheader festlegen, indem Sie unten auf die Registerkarte „Http Headers“ doppelklicken. Wir fügen Header mit einem JSON-Objekt hinzu. Um also jeder Anfrage ein Bearer-Token hinzuzufügen, würden Sie Folgendes hinzufügen:
{ "Authorization": "Bearer VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE" }
Hinweis: Ersetzen Sie das Bearer-Token durch das Token, das Sie beim Ausführen der Anmeldeabfrage erhalten haben.
Lassen Sie uns nun die me
-Abfrage ausführen:
{ me { email articles { id title } } }
Wir sollten eine Ausgabe erhalten, die so aussieht:
{ "data": { "me": { "email": "[email protected]", "articles": [ { "id": "1", "title": "Rerum perspiciatis et quos occaecati exercitationem." }, { "id": "2", "title": "Placeat quia cumque laudantium optio voluptatem sed qui." }, { "id": "3", "title": "Optio voluptatem et itaque sit animi." }, { "id": "4", "title": "Excepturi in ad qui dolor ad perspiciatis adipisci." }, { "id": "5", "title": "Qui nemo blanditiis sed fugit consequatur." } ] } } }
Middleware
Nachdem wir nun wissen, dass unsere Authentifizierung ordnungsgemäß funktioniert, erstellen wir unsere letzte Mutation, um einen Artikel mit dem aktuell authentifizierten Benutzer zu erstellen. Wir verwenden die @field
Direktive, um Lighthouse auf unseren Resolver zu verweisen, und wir fügen auch eine @middleware
Direktive hinzu, um sicherzustellen, dass ein Benutzer angemeldet ist.
type Mutation { # ... createArticle(title: String!, content: String!): Article @field(resolver: "ArticleMutator@create") @middleware(checks: ["auth:api"]) }
Lassen Sie uns zunächst eine Mutationsklasse generieren:
$ php artisan lighthouse:mutation ArticleMutator
Als Nächstes aktualisieren wir den Mutator mit der folgenden Logik:
namespace App\GraphQL\Mutations; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; class ArticleMutator { /** * Return a value for the field. * * @param null $rootValue * @param mixed[] $args * @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context * @return mixed */ public function create($rootValue, array $args, GraphQLContext $context) { $article = new \App\Article($args); $context->user()->articles()->save($article); return $article; } }
Hinweis: Wir haben create
resolve
. Sie müssen nicht für jeden Resolver eine neue Klasse erstellen. Stattdessen können Sie Logik gruppieren, wenn es sinnvoller ist.
Lassen Sie uns zum Schluss unsere neue Mutation ausführen und die Ausgabe überprüfen. Achten Sie darauf, den Authorization
Header aus unserer vorherigen Abfrage auf der Registerkarte „HTTP-Header“ beizubehalten:
mutation { createArticle( title:"Building a GraphQL Server with Laravel" content:"In case you're not currently familiar with it, GraphQL is a query language used to interact with your API..." ) { id author { id email } } }
Wir sollten die folgende Ausgabe erhalten:
{ "data": { "createArticle": { "id": "56", "author": { "id": "1", "email": "[email protected]" } } } }
Einpacken
Um es noch einmal zusammenzufassen: Wir haben Lighthouse genutzt, um einen GraphQL-Server für unser Laravel-Projekt zu erstellen. Wir haben einige eingebaute Schemadirektiven verwendet, Abfragen und Mutationen erstellt und die Autorisierung und Authentifizierung gehandhabt.
Mit Lighthouse können Sie viel mehr tun (z. B. das Erstellen eigener benutzerdefinierter Schemaanweisungen), aber für die Zwecke dieses Artikels haben wir uns an die Grundlagen gehalten und konnten einen GraphQL-Server mit relativ wenig Boilerplate zum Laufen bringen.
Wenn Sie das nächste Mal eine API für eine mobile oder Single-Page-Anwendung einrichten müssen, sollten Sie GraphQL als Möglichkeit zur Abfrage Ihrer Daten in Betracht ziehen!