Budowanie serwera GraphQL z Laravel

Opublikowany: 2022-03-11

Jeśli nadal go nie znasz, GraphQL jest językiem zapytań używanym do interakcji z Twoim API, który zapewnia pewne korzyści w porównaniu z alternatywnymi architekturami, takimi jak REST. GraphQL jest niezwykle przydatny, gdy służy jako punkt końcowy dla aplikacji mobilnych i jednostronicowych. GraphQL pozwala na stosunkowo łatwe wysyłanie zapytań do zagnieżdżonych i powiązanych danych w żądaniu, umożliwiając programistom uzyskanie dokładnych danych, których potrzebują w ramach jednej podróży w obie strony na serwer.

Laravel jest popularnym, upartym frameworkiem internetowym PHP. Zapewnia wiele wbudowanych narzędzi do szybkiego uruchamiania i uruchamiania aplikacji, ale także pozwala programistom na zamianę własnych implementacji na wbudowane interfejsy Laravela, gdy jest to preferowane.

Chociaż społeczności otaczające zarówno GraphQL, jak i Laravela znacznie się rozrosły, odkąd zostały one otwarte, dokumentacja wyjaśniająca, jak używać tych dwóch technologii razem, jest nadal dość skąpa.

Tak więc w tym tutorialu pokażę Ci jak stworzyć swój własny serwer GraphQL używając Laravela.

Przegląd projektu

Ilustracja przedstawiająca przegląd serwera GraphQL

Zanim zaczniemy, musimy zapoznać się z projektem, który próbujemy zbudować. W tym celu zdefiniujemy nasze zasoby i stworzymy nasz schemat GraphQL, którego później użyjemy do obsługi naszego API.

Zasoby projektu

Nasza aplikacja będzie składać się z dwóch zasobów: Artykuły i Użytkownicy . Zasoby te zostaną zdefiniowane jako typy obiektów w naszym schemacie GraphQL:

 type User { id: ID! name: String! email: String! articles: [Article!]! } type Article { id: ID! title: String! content: String! author: User! }

Patrząc na schemat, widzimy, że między naszymi dwoma obiektami istnieje relacja jeden-do-wielu. Użytkownicy mogą pisać wiele artykułów, a artykuł ma przypisanego autora (użytkownika).

Teraz, gdy mamy już zdefiniowane typy obiektów, będziemy potrzebować sposobu na tworzenie i wysyłanie zapytań do naszych danych, więc zdefiniujmy nasze obiekty zapytań i mutacji:

 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 }

Konfiguracja naszego projektu Laravel

Teraz, gdy zdefiniowaliśmy już nasz schemat GraphQL, zacznijmy uruchamiać nasz projekt Laravel. Zacznijmy od stworzenia nowego projektu Laravel via Composer:

 $ composer create-project --prefer-dist laravel/laravel laravel-graphql

Aby upewnić się, że wszystko działa, uruchommy nasz serwer i upewnijmy się, że widzimy domyślną stronę Laravela:

 $ cd laravel-graphql $ php artisan serve Laravel development server started: <http://127.0.0.1:8000>

Modele baz danych i migracje

Na potrzeby tego artykułu będziemy używać SQLite. Wprowadźmy więc następujące zmiany w domyślnym pliku .env :

 DB_CONNECTION=sqlite # DB_HOST= # DB_PORT= # DB_DATABASE=database.sqlite # DB_USERNAME= # DB_PASSWORD=

Następnie utwórzmy nasz plik bazy danych:

 $ touch ./database/database.sqlite

Laravel jest dostarczany z modelem użytkownika i kilkoma podstawowymi plikami migracji. Dodajmy szybko kolumnę api_token do naszego pliku migracji CreateUsersTable dostarczonego nam przez Laravela:

 /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'); } }

Wrócimy do tej dodatkowej kolumny w dalszej części artykułu, gdy przejdziemy do autoryzacji. Teraz przejdźmy dalej i stwórzmy nasz model artykułu oraz plik migracji, aby utworzyć powiązaną tabelę:

 $ php artisan make:model Article -m

Uwaga: opcja -m tworzy plik migracji dla naszego nowo utworzonego modelu artykułu.

Dokonajmy pewnych zmian w wygenerowanym pliku migracji:

 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'); } }

Dodaliśmy klucz obcy wskazujący na id w naszej tabeli users , a także kolumny title i content , które zdefiniowaliśmy w naszym schemacie GraphQL.

Teraz, gdy mamy już zdefiniowane pliki migracji, przejdźmy dalej i uruchom je w naszej bazie danych:

 $ php artisan migrate

Następnie zaktualizujmy nasze modele, definiując niezbędne relacje:

 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); } }

Rozsiewanie bazy danych

Teraz, gdy mamy już skonfigurowane modele i migracje, zacznijmy naszą bazę danych. Zaczniemy od stworzenia kilku klas seederów dla naszych articles i tabel users :

 $ php artisan make:seeder UsersTableSeeder $ php artisan make:seeder ArticlesTableSeeder

Następnie skonfigurujmy je tak, aby wstawiały fałszywe dane do naszej bazy danych SQLite:

 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); } }

Na koniec uruchommy nasze seedery bazy danych, aby pobrać dane do naszej bazy danych:

 $ php artisan db:seed

Latarnia morska Laravel i serwer GraphQL

Teraz, gdy mamy już skonfigurowaną bazę danych i modele, czas zacząć budować nasz serwer GraphQL. Obecnie dostępnych jest kilka rozwiązań dla Laravela, ale w tym artykule użyjemy Lighthouse.

Lighthouse to pakiet, który stworzyłem kilka lat temu i ostatnio otrzymałem niesamowite wsparcie od rosnącej społeczności wokół niego. Pozwala programistom na szybkie skonfigurowanie serwera GraphQL przy użyciu Laravela przy niewielkiej liczbie szablonów, a jednocześnie jest wystarczająco elastyczny, aby umożliwić programistom dostosowanie go do potrzeb niemal każdego projektu.

Ilustracja latarni morskiej Laravel i serwera GraphQL

Zacznijmy od wciągnięcia pakietu do naszego projektu:

 $ composer require nuwave/lighthouse:"3.1.*"

Następnie opublikujmy plik konfiguracyjny Lighthouse:

 $ php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config

Uwaga: możesz także opublikować domyślny plik schematu Lighthouse, po prostu usuwając opcję --tag=config . Ale na potrzeby tego artykułu stworzymy nasz plik schematu od podstaw.

Jeśli spojrzymy na plik config/lighthouse.php , zauważysz ustawienie używane do zarejestrowania naszego pliku schematu w Lighthouse:

 'schema' => [ 'register' => base_path('graphql/schema.graphql'), ],

Stwórzmy więc nasz plik schematu i skonfigurujmy typ obiektu użytkownika i zapytanie:

 $ 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 }

Zauważysz, że nasz schemat wygląda podobnie do tego, który zdefiniowaliśmy wcześniej, z wyjątkiem tego, że dodaliśmy pewne identyfikatory zwane dyrektywami schematu.

Poświęćmy chwilę na rozbicie naszego zdefiniowanego schematu. Naszą pierwszą definicją jest typ obiektu o nazwie User , który ma związek z naszym wymownym modelem App\User . Zdefiniowaliśmy id , name i email jako pola, które można odpytywać z naszych modeli User . Alternatywnie oznacza to, że kolumny password , created_at i updated_at są polami, o które nie można zapytać z naszego API.

Następnie mamy nasz typ Query , który jest punktem wejścia do naszego API i może być użyty do zapytania o dane. Naszym pierwszym polem jest pole users , które zwraca tablicę typów obiektów User . Dyrektywa @all nakazuje Lighthouse uruchomienie kwerendy Eloquent przy użyciu naszego modelu User i uzyskanie wszystkich wyników. Byłoby to to samo, co uruchomienie następującego:

 $users = \App\User::all();

Uwaga: Lighthouse wie, że należy szukać modelu w przestrzeni nazw \App\User ze względu na opcję namespaces zdefiniowaną w swoim pliku konfiguracyjnym.

Drugie zdefiniowane pole w naszym typie zapytania to call user , które przyjmuje jako argument id i zwraca pojedynczy typ obiektu User . Dodaliśmy również dwie dyrektywy, aby pomóc Lighthouse automatycznie zbudować dla nas zapytanie i zwrócić pojedynczy model User . Dyrektywa @eq mówi Lighthouse, aby dodał gdzie w naszej kolumnie id , a dyrektywa @find nakazuje Lighthouse zwrócić pojedynczy wynik. Aby napisać to zapytanie za pomocą kreatora zapytań Laravela, wyglądałoby to tak:

 $user = \App\User::where('id', $args['id'])->first();

Wysyłanie zapytań do naszego GraphQL API

Teraz, gdy mamy trochę wglądu w to, jak Lighthouse używa naszego schematu do tworzenia zapytań, uruchommy nasz serwer i zacznijmy odpytywać dane. Zaczniemy od uruchomienia naszego serwera:

 $ php artisan serve Laravel development server started: <http://127.0.0.1:8000>

Aby wysłać zapytanie do punktu końcowego GraphQL, możesz uruchomić polecenie cURL w terminalu lub standardowym kliencie, takim jak Postman. Jednak, aby uzyskać pełne korzyści z GraphQL (takie jak autouzupełnianie, podświetlanie błędów, dokumentacja itp., użyjemy GraphQL Playground (wersja do pobrania tutaj).

Uruchamiając Playground, kliknij zakładkę „URL Endpoint” i wpisz http://localhost:8000/graphql, aby skierować GraphQL Playground do naszego serwera. Po lewej stronie edytora możemy odpytywać o nasze dane, więc zacznijmy od zapytania o wszystkich użytkowników, którym zasililiśmy bazę danych:

 { users { id email name } }

Kiedy naciśniesz przycisk odtwarzania na środku IDE (lub klikniesz Ctrl+Enter ), zobaczysz po prawej stronie wyjście JSON naszego serwera, które będzie wyglądać mniej więcej tak:

 { "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" }, ... ] } }

Uwaga: Ponieważ użyliśmy Fakera do seedowania naszej bazy danych, dane w polach e- email i name będą inne.

Spróbujmy teraz zapytać o jednego użytkownika:

 { user(id: 1) { email name } }

I otrzymamy następujące dane wyjściowe dla pojedynczego użytkownika:

 { "data": { "user": { "email": "[email protected]", "name": "Carolyn Powlowski" } } }

Zapytania o takie dane są przyjemne na początek, ale jest bardzo mało prawdopodobne, że będziesz w projekcie, w którym kiedykolwiek chciałbyś zapytać o wszystkie swoje dane, więc spróbujmy dodać trochę paginacji. Przeglądając szeroką gamę wbudowanych dyrektyw Lighthouse, mamy łatwo dostępną dyrektywę @paginate , więc zaktualizujmy obiekt zapytania naszego schematu w następujący sposób:

 type Query { user(id: ID! @eq): User @find users: [User!]! @paginate }

Jeśli ponownie załadujemy GraphQL Playground ( Ctrl/Cmd + R ) i spróbujemy ponownie zapytać naszych users , zauważysz, że otrzymujemy komunikat o błędzie z informacją, że Cannot query field "id" on type "UserPaginator" , więc co się stało? Za kulisami Lighthouse manipuluje naszym schematem, abyśmy mogli uzyskać podzielony na strony zestaw wyników, zmieniając typ zwracany pola users .

Przyjrzyjmy się bliżej, sprawdzając nasz schemat w zakładce „Dokumenty” w GraphQL Playground. Jeśli spojrzysz na pole users , to zwraca UserPaginator , który zwraca tablicę użytkowników i zdefiniowany w Lighthouse typ PaginatorInfo :

 type UserPaginator { paginatorInfo: PaginatorInfo! data: [User!]! } type PaginatorInfo { count: Int! currentPage: Int! firstItem: Int hasMorePages: Boolean! lastItem: Int lastPage: Int! perPage: Int! total: Int! }

Jeśli znasz wbudowaną paginację Laravela, pola dostępne w typie PaginatorInfo będą prawdopodobnie wyglądać bardzo znajomo. Tak więc, aby wysłać zapytanie dla dwóch użytkowników, uzyskać całkowitą liczbę użytkowników w systemie i sprawdzić, czy mamy więcej stron do przejścia, wyślemy następujące zapytanie:

 { users(count:2) { paginatorInfo { total hasMorePages } data { id name email } } }

Co da nam następującą odpowiedź:

 { "data": { "users": { "paginatorInfo": { "total": 11, "hasMorePages": true }, "data": [ { "id": "1", "name": "Carolyn Powlowski", "email": "[email protected]" }, { "id": "2", "name": "Elouise Raynor", "email": "[email protected]" }, ] } } }

Relacje

Ogólnie rzecz biorąc, podczas tworzenia aplikacji wiele danych jest powiązanych. W naszym przypadku User może napisać wiele Articles , więc dodajmy tę relację do naszego typu użytkownika i zdefiniujmy nasz typ Article :

 type User { id: ID! name: String! email: String! articles: [Article!]! @hasMany } type Article { id: ID! title: String! content: String! }

W tym przypadku używamy innej dyrektywy schematu dostarczonej przez Lighthouse @hasMany , która informuje Lighthouse, że nasz model User ma relację \Illuminate\Database\Eloquent\Relations\HasMany z modelem Article .

Teraz przeanalizujmy naszą nowo zdefiniowaną relację:

 { user(id:1) { articles { id title } } }

To zapewni nam następującą odpowiedź:

 { "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." } ] } } }

Na koniec odwróćmy naszą relację i dodajmy relację author do naszego typu obiektu Article za pomocą dyrektywy schematu Lighthouse @belongsTo oraz uaktualniając nasze Query :

 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 }

Zobaczysz, że dodaliśmy opcjonalny argument relation do dyrektywy @belongsTo . To mówi Lighthouse, aby użył relacji user modelu Articles i przypisał ją do pola author .

Teraz zapytajmy o listę artykułów i zdobądźmy powiązanego autora:

 { articles(count:2) { paginatorInfo { total hasMorePages } data { id title author { name email } } } }

A z naszego serwera powinniśmy pobrać:

 { "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]" } } ] } } }

Mutacja GraphQL

Teraz, gdy możemy przeszukiwać nasze dane, stwórzmy kilka mutacji, aby utworzyć nowych użytkowników i artykuły. Zaczniemy od naszego modelu użytkownika:

 type Mutation { createUser( name: String! email: String! @rules(apply: ["email", "unique:users"]) password: String! @bcrypt @rules(apply: ["min:6"]) ): User @create }

Teraz podzielmy tę definicję schematu. Stworzyliśmy mutację o nazwie createUser , która przyjmuje trzy argumenty ( name , email i password ). Zastosowaliśmy dyrektywę @rules zarówno do naszych argumentów dotyczących adresu e- email , jak i password . Może to wyglądać trochę znajomo, ponieważ jest podobne do logiki walidacji, którą Laravel zapewnia dla swoich kontrolerów.

Następnie dołączyliśmy dyrektywę @bcrypt do naszego pola password . Spowoduje to zaszyfrowanie hasła przed przekazaniem go do nowo utworzonego modelu.

Wreszcie, aby pomóc nam w tworzeniu nowych modeli, Lighthouse udostępnia dyrektywę @create schema, która wykorzysta zdefiniowane przez nas argumenty i utworzy nowy model. Wykonanie tej samej logiki w kontrolerze wyglądałoby następująco:

 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]); } }

Teraz, gdy mamy już skonfigurowane pole mutacji createUser, przejdźmy dalej i uruchommy je w GraphQL Playground, wykonując następujące czynności:

 mutation { createUser( name:"John Doe" email:"[email protected]" password: "secret" ) { id name email } }

Powinniśmy otrzymać następujący wynik:

 { "data": { "createUser": { "id": "12", "name": "John Doe", "email": "[email protected]" } } }

Uwierzytelnianie i autoryzacja GraphQL

Ponieważ musimy dodać user_id do naszych modeli Article , teraz byłby świetny czas, aby przejść do uwierzytelniania i autoryzacji w GraphQL/Lighthouse.

tekst alternatywny obrazu

Aby uwierzytelnić użytkownika, musimy dostarczyć mu api_token , więc stwórzmy mutację, aby to obsłużyć i dodamy dyrektywę @field , aby wskazać Lighthouse niestandardowy resolver, który obsłuży logikę. Ustawiamy przelicznik według tego samego wzorca, co definiowanie kontrolera w Laravelu za pomocą argumentu resolver .

Z dyrektywą @field zdefiniowaną poniżej, informujemy Lighthouse o uruchomieniu mutacji login , użyj metody createToken w naszej App\GraphQL\Mutations\AuthMutator :

 type Mutation { # ... login( email: String! password: String! ): String @field(resolver: "AuthMutator@resolve") }

Uwaga: nie musisz tutaj uwzględniać całej przestrzeni nazw. W pliku konfiguracyjnym lighthouse.php zobaczysz, że mamy już zdefiniowaną przestrzeń nazw dla naszych mutacji jako App\\GraphQL\\Mutations — jednak możesz użyć pełnej przestrzeni nazw, jeśli wolisz.

Wykorzystajmy generator Lighthouse'a do stworzenia nowej klasy mutatora:

 $ php artisan lighthouse:mutation AuthMutator

Następnie zaktualizujmy naszą funkcję przelicznika w następujący sposób:

 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; } }

Teraz, gdy mamy już skonfigurowany resolver, przetestujmy go i spróbujmy uzyskać token API za pomocą następującej mutacji w GraphQL Playground:

 mutation { login(email:"[email protected]", password:"secret") }

Powinniśmy otrzymać odesłany token w następujący sposób:

 { "data": { "login": "VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE" } }

Uwaga: Pamiętaj, aby skopiować token zwrócony z mutacji loginu, abyśmy mogli go później użyć.

Następnie dodajmy pole zapytania, które zwróci uwierzytelnionego użytkownika, aby upewnić się, że nasza logika działa. Dodamy pole o nazwie me i użyjemy dyrektywy @auth Lighthouse, aby zwrócić aktualnie uwierzytelnionego użytkownika. Ustawimy również argument guard równy api , ponieważ w ten sposób będziemy uwierzytelniać użytkownika.

 type Query { # ... me: User @auth(guard: "api") }

Teraz uruchommy zapytanie. W GraphQL Playground możesz ustawić nagłówki żądań, klikając dwukrotnie kartę "Nagłówki HTTP" na dole. Dodajemy nagłówki z obiektem JSON, więc aby dodać token okaziciela do każdego żądania, należy dodać następujące elementy:

 { "Authorization": "Bearer VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE" }

Uwaga: Zastąp token okaziciela tokenem otrzymanym podczas uruchamiania zapytania logowania .

Teraz uruchommy zapytanie do me :

 { me { email articles { id title } } }

Powinniśmy otrzymać dane wyjściowe, które wyglądają tak:

 { "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." } ] } } }

Oprogramowanie pośredniczące

Teraz, gdy wiemy, że nasze uwierzytelnianie działa poprawnie, utwórzmy naszą ostatnią mutację, aby utworzyć artykuł przy użyciu aktualnie uwierzytelnionego użytkownika. Użyjemy dyrektywy @field , aby skierować Lighthouse do naszego resolvera, a także dołączymy dyrektywę @middleware , aby upewnić się, że użytkownik jest zalogowany.

 type Mutation { # ... createArticle(title: String!, content: String!): Article @field(resolver: "ArticleMutator@create") @middleware(checks: ["auth:api"]) }

Najpierw wygenerujmy klasę mutacji:

 $ php artisan lighthouse:mutation ArticleMutator

Następnie zaktualizujmy mutator za pomocą następującej logiki:

 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; } }

Uwaga: zmieniliśmy nazwę domyślnej funkcji resolve na create . Nie musisz tworzyć nowej klasy dla każdego resolvera. Zamiast tego możesz zgrupować logikę, jeśli ma to więcej sensu.

Na koniec uruchommy naszą nową mutację i sprawdźmy wyniki. Pamiętaj, aby zachować nagłówek Authorization z naszego poprzedniego zapytania w zakładce „Nagłówki HTTP”:

 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 } } }

Powinniśmy otrzymać następujący wynik:

 { "data": { "createArticle": { "id": "56", "author": { "id": "1", "email": "[email protected]" } } } }

Zawijanie

Podsumowując, wykorzystaliśmy Lighthouse do stworzenia serwera GraphQL dla naszego projektu Laravel. Korzystaliśmy z niektórych wbudowanych dyrektyw schematu, tworzyliśmy zapytania i mutacje oraz obsługiwaliśmy autoryzację i uwierzytelnianie.

Lighthouse pozwala zrobić znacznie więcej (np. stworzyć własne niestandardowe dyrektywy schematu), ale na potrzeby tego artykułu trzymaliśmy się podstaw i byliśmy w stanie uruchomić serwer GraphQL z dość niewielką ilością standardowych.

Następnym razem, gdy będziesz musiał skonfigurować API dla aplikacji mobilnej lub aplikacji jednostronicowej, pamiętaj, aby rozważyć GraphQL jako sposób na zapytanie o swoje dane!

Powiązane: Pełne uwierzytelnianie użytkownika i kontrola dostępu – samouczek dotyczący paszportu Laravel, Pt. 1