Budowanie serwera GraphQL z Laravel
Opublikowany: 2022-03-11Jeś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
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.
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.
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!