برنامه‌نویسی

آموزش توسعه‌ی GraphQL API با فریم‌ورک Laravel


۱۱ خرداد ۱۴۰۰
آموزش توسعه‌ی graphql api با فریم‌ورک laravel

اگر شما هم به‌تازگی مسیر خود را به‌عنوان یک توسعه دهنده‌ی بک‌اند شروع کرده‌اید ممکن است کار با تکنولوژی‌هایی مانند GraphQL و Docker ترسناک به‌نظر برسد. به‌همین منظور می‌خواهیم در این مقاله به شما نحوه‌ی ایجاد GraphQL API با فریم‌ورک Laravel را آموزش دهیم تا متوجه شوید یادگیری این تکنولوژی‌ها از آنچه که فکر می‌کرده‌اید بسیار ساده‌تر است.

البته باید اضافه کرد که در این مقاله به ریزجزئیات کار با فریم‌ورک Laravel پرداخته نمی‌شود و شما می‌توانید با مطالعه‌ی مقاله‌ی آموزش مقدماتی فریم‌ورک Laravel به مباحث مقدماتی این فریم‌ورک تسلط پیدا کنید.

پیش نیازها

پیش‌نیازهای ادامه‌ی این مقاله به شرح زیر است و قبل از شروع کار باید از نصب بودن موارد زیر اطمینان حاصل کنید:

  • +PHP 7
  • Composer 2.0
  • Docker
  • Docker-Compose

همچنین فرض را بر این می‌گیریم که شما با مقدمات زبان PHP، فریم‌ورک Laravel و تئوری GraphQL آشنا هستید.

توضیحاتی درباره‌ی برنامه‌ی نهایی

این پروژه بسیار ساده فقط از دو Model با نام‌های Quests و Categories تشکیل شده که Schema دیتابیس آن به شکل زیر است و شما می‌توانید در انتهای این مقاله عملیات CRUD را با استفاده از GraphQL API بر روی این برنامه‌ی ساده اجرا کنید.

schema ساده‌ی دیتابیس برای ایجاد graphql api با استفاده از فریم‌ورک laravel

پیاده‌سازی ساختار اولیه‌ی پروژه

ما برای شروع به یک پروژه‌ی Laravel نیاز داریم بنابراین با اجرای دستور زیر یک پروژه‌ی Laravel با نام quest_journal ایجاد می‌کنیم:

composer create-project laravel/laravel quest_journal

با اجرای موفقیت‌آمیز دستور فوق یک پوشه‌ی جدید با نام quest_journal در مسیر فعلی Terminal ایجاد خواهد شد و زمان آن فرا می‌رسد که برای پیکربندی Laravel Sail وارد مسیر پروژه شویم:

# Move into the project
cd quest_journal

# Install and configure laravel sail
php artisan sail:install

درهنگام نصب Laravel Sail از شما پرسیده می‌شود که می‌خواهید کدام Service را نصب کنید که با فشردن دکمه‌ی Enter فقط MySQL نصب خواهد شد. پس از نصب Laravel Sail یک فایل جدید با نام docker-compose.yml در مسیر پروژه ایجاد خواهد شد که به ما کمک می‌کند Containerهای مورد نیاز خود را با اجرای دستورهای زیر راه‌اندازی و اجرا کنیم:

# Run the containers
./vendor/bin/sail up -d

# Check if the containers are running
docker ps

اگر راه‌اندازی Containerها بدون خطا انجام شده باشد می‌توانید آدرس localhost را در مرورگر خود باز کنید و صفحه‌ی پیش‌فرض فریم‌ورک Laravel برای شما نمایش داده خواهد شد.

صفحه‌ی پیش‌فرض فریم‌ورک laravel

اما همان‌طور که مشاهده می‌کنید برای استفاده از Laravel Sail باید مسیردهی فایل اجرایی را هر بار تکرار کنیم و برای مدیریت این موضوع از alias کمک خواهیم گرفت:

# in ~./zshrc or ~./bashrc

alias sail = 'bash vendor/bin/sail'

تا اینجای کار می‌توان گفت که پیاده‌سازی ساختار اولیه پروژه انجام شده اما قبل از رفتن به مراحل بعدی باید چند پکیج را نصب کنیم:

# IDE helper for laravel, always useful to have.
sail composer require --dev barryvdh/laravel-ide-helper

# GraphQL library which we are going to use
sail composer require rebing/graphql-laravel

درنهایت پس از نصب شدن rebing/graphql-laravel می‌توانیم با اجرای دستور زیر فایل پیکربندی GraphQL را در مسیر config/graphql.php اضافه کنیم:

sail artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider"

ایجاد Modelها و Migrationها

بدون فوت وقت و توضیحات اضافی درباره‌ی نحوه‌ی ایجاد یک Model در فریم‌ورک Laravel به‌سراغ اضافه کردن Category Model می‌رویم:

# Create model with migrations
sail artisan make:model -m Category

پس از اجرای دستور فوق، فایل‌های مربوط به Model و Migration مورد نظر ما به‌ترتیب در مسیرهای App\Models و database/migrations ایجاد خواهد شد و ما در مرحله‌ی اول فیلد‌های مورد نظر خود را به Migration ایجاد شده اضافه خواهیم کرد:

<?php

// database\migrations\yyyy_mm_dd_hhMMss_create_categories_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCategoriesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->text('title');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('categories');
    }
}

پس از اضافه کردن فیلد‌ها به‌سراغ پیاده‌سازی Model ایجاد شده می‌رویم:

<?php

// App\Models\Category.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    use HasFactory;
		
	protected $fillable = ['title'];

    public function quests(){
        return $this->hasMany(Quest::class);
    }
}

البته پس از اضافه کردن کدهای فوق با یک خطا در رابطه با Quest::class روبرو خواهید شد اما جای نگرانی نیست. در قدم بعد با اجرای دستور زیر، Quest Model به پروژه اضافه خواهد شد:

sail artisan make:model -m Quest

اول به‌سراغ فایل Migration می‌رویم و آن را به‌شکل زیر تغییر می‌دهیم:

<?php


// database\migrations\yyyy_mm_dd_hhMMss_create_quests_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateQuestsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('quests', function (Blueprint $table) {
            $table->id();
            $table->text('title');
            $table->text('description');
            $table->integer('reward');
            $table->foreignId('category_id')->constrained();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('quests');
    }
}

و پس از تغییر فایل Migration مربوط به Quest به‌سراغ فایل Model آن می‌رویم:

<?php

// App\Models\Quest

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Quest extends Model
{
    use HasFactory;
		
	protected $fillable = ['title', 'category_id', 'description', 							  'reward'];

    public function category(){
        return $this->belongsTo(Category::class);
    }
}

درنهایت دستور زیر را برای اعمال همه‌ی تغییرها در دیتابیس برنامه اجرا خواهیم کرد:

# Apply migrations
sail artisan migrate

Seed کردن دیتابیس

به‌عنوان برنامه‌نویسانی که در وارد کردن اطلاعات به دیتابیس بسیار تنبل هستند می‌خواهیم با اجرای دستورهای زیر از Factoryها برای وارد کردن اطلاعات به جدول‌های Quest و Category کمک بگیریم:

# Create a factory class for quest model
sail artisan make:factory QuestFactory --model=Quest

# Create a factory class for category model
sail artisan make:factory CategoryFactory --model=Category

در قدم اول به‌سراغ QuestFactory در مسیر database/factories می‌رویم که قرار است به ما در ایجاد Questهای fake کمک کند:

<?php

namespace Database\Factories;

use App\Models\Category;
use App\Models\Quest;
use Illuminate\Database\Eloquent\Factories\Factory;

class QuestFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Quest::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        $categoryIDs = Category::all()->pluck('id')->toArray();

        return [
            'title' => $this->faker->title(),
            'description' => $this->faker->text(),
            'reward' => $this->faker->numberBetween(1, 100),
            'category_id' => $this->faker->randomElement($categoryIDs)
        ];
    }
}

و در قدم بعد CategoryFactory را پیاده‌سازی خواهیم کرد که به‌طور کلی عملکرد آن ساده‌تر است و فقط باید titleهای مورد نیاز را برای ما ایجاد کند:

<?php

namespace Database\Factories;

use App\Models\Category;
use Illuminate\Database\Eloquent\Factories\Factory;

class CategoryFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Category::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'title' => $this->faker->title()
        ];
    }
}

درنهایت به‌جای ایجاد seeder می‌توانیم از classهای QuestFactory و CategoryFactory در فایل DatabaseSeeder.php برای ایجاد داده‌های خام استفاده کنیم:

<?php

// database\seeders\DatabaseSeeder.php

namespace Database\Seeders;

use App\Models\Category;
use App\Models\Quest;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        Category::factory(10)->create();
        Quest::factory(10)->create();
    }
}

حال برای اعمال تغییرهای به‌وجود آمده باید دستور زیر را اجرا کنید:

sail artisan db:seed

ساختار پوشه‌ی GraphQL

ما در این مرحله آماده‌ی توسعه‌ی GraphQL API هستیم اما در ابتدا باید پوشه‌ی GraphQL را در پوشه‌ی app ایجاد کنیم. در مرحله‌ی بعد بایستی پوشه‌های Mutations، Queries و Types را در پوشه‌ی GraphQL ایجاد کنیم تا ساختار نهایی پوشه‌ها به شکل زیر باشد.

پوشه‌ی graphql

قبل از شروع کدنویسی بهتر است هدف هر پوشه را توضیح دهیم:

  • Mutations: این پوشه شامل Classهایی می‌شود که عملیات insert، update و delete را مدیریت می‌کنند.
  • Queries: در این پوشه Classهایی قرار داده می‌شود که برخی داده‌های مورد نیاز ما را از دیتابیس برنامه بازیابی می‌کنند.
  • Types: شما می‌توانید این پوشه را جایی برای تعریف برخی Modelها درنظر بگیرید که نوع objectهابی که می‌توانند از دیتابیس دریافت شوند را تعیین می‌کنند.

چگونگی تعریف Typeها

ما در پوشه‌ی Types دو Class با نام‌های CategoryType و QuestType ایجاد خواهیم کرد و کاربرد پکیج rebing/graphql-laravel اینجا مشخص خواهد شد:

<?php

// app\GraphQL\Types\CategoryType.php

namespace App\GraphQL\Types;

use App\Models\Category;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;

class CategoryType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Category',
        'description' => 'Collection of categories',
        'model' => Category::class
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'ID of quest'
            ],
            'title' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'Title of the quest'
            ],
            'quests' => [
                'type' => Type::listOf(GraphQL::type('Quest')),
                'description' => 'List of quests'
            ]
        ];
    }
}
<?php

// app\GraphQL\Types\QuestType.php

namespace App\GraphQL\Types;

use App\Models\Quest;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;

class QuestType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Quest',
        'description' => 'Collection of quests with their respective category',
        'model' => Quest::class
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'ID of quest'
            ],
            'title' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'Title of the quest'
            ],
            'description' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'Description of quest'
            ],
            'reward' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'Quest reward'
            ],
            'category' => [
                'type' => GraphQL::type('Category'),
                'description' => 'The category of the quest'
            ]
        ];
    }
}

attributes شامل اطلاعات اصلی درمورد Type و Model مورد استفاده‌ی ما است و fields تمام فیلد‌هایی که کاربر می‌تواند درخواست کند را return می‌کند.

چگونگی تعریف Queryها

پس از تعریف Typeها به‌سراغ تعریف Queryها می‌رویم و برای هر Model، دو کوئری خواهیم داشت که مجموعا تعداد کوئری‌های ما را به چهار عدد می‌رساند:

  • QuestQuery
  • QuestsQuery
  • CategoryQuery
  • CategoriesQuery

و ما این Queryها را به‌شکل زیر در پوشه‌ی Queries قرار می‌دهیم.

پوشه‌ی queries

پس از ایجاد همه‌ی فایل‌ها می‌توانید کدهای مربوط به هر Query را در فایل مربوطه قرار دهید:

<?php

// app\GraphQL\Queries\Category\CategoriesQuery.php

namespace App\GraphQL\Queries\Category;


use App\Models\Category;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class CategoriesQuery extends Query
{
    protected $attributes = [
        'name' => 'categories',
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type('Category'));
    }

    public function resolve($root, $args)
    {
        return Category::all();
    }
}
<?php


// app\GraphQL\Queries\Category\CategoryQuery.php

namespace App\GraphQL\Queries\Category;


use App\Models\Category;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class CategoryQuery extends Query
{
    protected $attributes = [
        'name' => 'category',
    ];

    public function type(): Type
    {
        return GraphQL::type('Category');
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' => Type::int(),
                'rules' => ['required']
            ]
        ];
    }

    public function resolve($root, $args)
    {
        return Category::findOrFail($args['id']);
    }
}
<?php


// app\GraphQL\Queries\Quest\QuestQuery.php

namespace App\GraphQL\Queries\Quest;


use App\Models\Quest;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class QuestQuery extends Query
{
    protected $attributes = [
        'name' => 'quest',
    ];

    public function type(): Type
    {
        return GraphQL::type('Quest');
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' => Type::int(),
                'rules' => ['required']
            ]
        ];
    }

    public function resolve($root, $args)
    {
        return Quest::findOrFail($args['id']);
    }
}
<?php

// app\GraphQL\Queries\Quest\QuestsQuery.php

namespace App\GraphQL\Queries\Quest;


use App\Models\Quest;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class QuestsQuery extends Query
{
    protected $attributes = [
        'name' => 'quests',
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type('Quest'));
    }

    public function resolve($root, $args)
    {
        return Quest::all();
    }
}

برای درک بهتر کدهای فوق باید گفت که از فانکشن attributes برای پیکربندی Queryها و از فانکشن type برای تعیین نوع objectهایی که return می‌شوند استفاده می‌شود. در فانکشن args نیز پارامترهای قابل قبول تعریف می‌شود که ما فقط به id هر Quest نیاز داریم و درنهایت object مورد نظر ما در فانکشن resolve با استفاده از eloquent برگشت داده خواهد شد.

چگونگی تعریف Mutationها

ما برای هر Model به سه Class مختلف برای انجام عملیات Insert، Update و Delete نیاز خواهیم داشت که مجموع Classهای ما را به شش عدد می‌رساند:

  • CreateCategoryMutation
  • DeleteCategoryMutation
  • UpdateCategoryMutation
  • CreateQuestMutation
  • DeleteQuestMutation
  • UpdateQuestMutation
پوشه‌ی mutations
<?php

// app\GraphQl\Mutations\Category\CreateCategoryMutation.php

namespace App\GraphQL\Mutations\Category;

use App\Models\Category;
use Rebing\GraphQL\Support\Mutation;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;

class CreateCategoryMutation extends Mutation
{
    protected $attributes = [
        'name' => 'createCategory',
        'description' => 'Creates a category'
    ];

    public function type(): Type
    {
        return GraphQL::type('Category');
    }

    public function args(): array
    {
        return [
            'title' => [
                'name' => 'title',
                'type' =>  Type::nonNull(Type::string()),
            ],
        ];
    }

    public function resolve($root, $args)
    {
        $category = new Category();
        $category->fill($args);
        $category->save();

        return $category;
    }
}
<?php

// app\GraphQl\Mutations\Category\DeleteCategoryMutation.php

namespace App\GraphQL\Mutations\Category;

use App\Models\Category;
use Rebing\GraphQL\Support\Mutation;
use GraphQL\Type\Definition\Type;

class DeleteCategoryMutation extends Mutation
{
    protected $attributes = [
        'name' => 'deleteCategory',
        'description' => 'deletes a category'
    ];

    public function type(): Type
    {
        return Type::boolean();
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' => Type::int(),
                'rules' => ['required']
            ]
        ];
    }

    public function resolve($root, $args)
    {
        $category = Category::findOrFail($args['id']);

        return  $category->delete() ? true : false;
    }
}
<?php

// app\GraphQl\Mutations\Category\UpdateCategoryMutation.php

namespace App\GraphQL\Mutations\Category;


use App\Models\Category;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Mutation;

class UpdateCategoryMutation extends Mutation
{
    protected $attributes = [
        'name' => 'updateCategory',
        'description' => 'Updates a category'
    ];

    public function type(): Type
    {
        return GraphQL::type('Category');
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' =>  Type::nonNull(Type::int()),
            ],
            'title' => [
                'name' => 'title',
                'type' =>  Type::nonNull(Type::string()),
            ],
        ];
    }

    public function resolve($root, $args)
    {
        $category = Category::findOrFail($args['id']);
        $category->fill($args);
        $category->save();

        return $category;
    }
}
<?php

// app\GraphQl\Mutations\Quest\CreateQuestMutation.php

namespace App\GraphQL\Mutations\Quest;

use App\Models\Quest;
use Rebing\GraphQL\Support\Mutation;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;

class CreateQuestMutation extends Mutation
{
    protected $attributes = [
        'name' => 'createQuest',
        'description' => 'Creates a quest'
    ];

    public function type(): Type
    {
        return GraphQL::type('Quest');
    }

    public function args(): array
    {
        return [
            'title' => [
                'name' => 'title',
                'type' =>  Type::nonNull(Type::string()),
            ],
            'description' => [
                'name' => 'description',
                'type' =>  Type::nonNull(Type::string()),
            ],
            'reward' => [
                'name' => 'reward',
                'type' => Type::nonNull(Type::int()),
            ],
            'category_id' => [
                'name' => 'category_id',
                'type' => Type::nonNull(Type::int()),
                'rules' => ['exists:categories,id']
            ]
        ];
    }

    public function resolve($root, $args)
    {
        $quest = new Quest();
        $quest->fill($args);
        $quest->save();

        return $quest;
    }
}
<?php

// app\GraphQl\Mutations\Quest\DeleteQuestMutation.php

namespace App\GraphQL\Mutations\Quest;

use App\Models\Quest;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;

class DeleteQuestMutation extends Mutation
{
    protected $attributes = [
        'name' => 'deleteQuest',
        'description' => 'Deletes a quest'
    ];

    public function type(): Type
    {
        return Type::boolean();
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' => Type::nonNull(Type::int()),
                'rules' => ['exists:quests']
            ]
        ];
    }

    public function resolve($root, $args)
    {
        $category = Quest::findOrFail($args['id']);

        return  $category->delete() ? true : false;
    }
}
<?php

// app\GraphQl\Mutations\Quest\UpdateQuestMutation.php

namespace App\GraphQL\Mutations\Quest;

use App\Models\Quest;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Mutation;

class UpdateQuestMutation extends Mutation
{
    protected $attributes = [
        'name' => 'updateQuest',
        'description' => 'Updates a quest'
    ];

    public function type(): Type
    {
        return GraphQL::type('Quest');
    }

    public function args(): array
    {
        return [
            'id' => [
                'name' => 'id',
                'type' =>  Type::nonNull(Type::int()),
            ],
            'title' => [
                'name' => 'title',
                'type' =>  Type::nonNull(Type::string()),
            ],
            'description' => [
                'name' => 'description',
                'type' =>  Type::nonNull(Type::string()),
            ],
            'reward' => [
                'name' => 'reward',
                'type' => Type::nonNull(Type::int()),
            ],
            'category_id' => [
                'name' => 'category_id',
                'type' => Type::nonNull(Type::int()),
                'rules' => ['exists:categories,id']
            ]
        ];
    }

    public function resolve($root, $args)
    {
        $quest = Quest::findOrFail($args['id']);
        $quest->fill($args);
        $quest->save();

        return $quest;
    }
}

فایل پیکربندی GraphQL

در نهایت پس از توسعه‌ی Queryها، Mutationها و Typeها باید همه‌ی آن‌ها را در فایل config/graphql.php که فایل پیکربندی GraphQL محسوب می‌شود، اضافه کرد:

<?php

return [
    // ... some code
    'schemas' => [
        'default' => [
            'query' => [
                'quest' => \App\GraphQL\Queries\Quest\QuestQuery::class,
                'quests' => \App\GraphQL\Queries\Quest\QuestsQuery::class,
                'category' => \App\GraphQL\Queries\Category\CategoryQuery::class,
                'categories' => \App\GraphQL\Queries\Category\CategoriesQuery::class,
            ],
            'mutation' => [
                'createQuest' => \App\GraphQL\Mutations\Quest\CreateQuestMutation::class,
                'updateQuest' => \App\GraphQL\Mutations\Quest\UpdateQuestMutation::class,
                'deleteQuest' => \App\GraphQL\Mutations\Quest\DeleteQuestMutation::class,
                'createCategory' => \App\GraphQL\Mutations\Category\CreateCategoryMutation::class,
                'updateCategory' => \App\GraphQL\Mutations\Category\UpdateCategoryMutation::class,
                'deleteCategory' => \App\GraphQL\Mutations\Category\DeleteCategoryMutation::class,
            ],
            'middleware' => [],
            'method' => ['get', 'post'],
        ],
    ],

    'types' => [
        'Quest' => \App\GraphQL\Types\QuestType::class,
        'Category' => \App\GraphQL\Types\CategoryType::class
     ],
    // some code 
];

تست برنامه‌ی نهایی

اگر Docker container شما متوقف نشده باشد می‌توانید آدرس http://localhost/graphiql را در مرورگر خود باز کرده و کوئری‌‌های دلخواه خود را اجرا کنید.

داشبورد graphiql

منبع: https://www.freecodecamp.org/news/build-a-graphql-api-using-laravel/