10 Simple Steps to Build & Test Laravel GraphQL API


Laravel

Author: Steve Alila
Reviewer: Deepak Prasad

In this tutorial, we’ll be setting up a GraphQL API in Laravel. We’ll cover lots of ground including project initialization, creating models and migrations, installing GraphQL packages, and implementing schemas. In addition to that, we’ll also look at setting up queries and mutations. Furthermore, we’ll dive into registering types, optional database seeding, configuring routes and finally testing the API for functionality.

Laravel is an amazing framework. And as great as it is at building APIs, there's always room for improvement. The good news is that Laravel doesn't need to work alone! By utilizing the power of GraphQL alongside Laravel’s powerful features you get something really special! Developed by Facebook, GraphQL offers us the ability to fetch only the data we need from an API. Dynamic data gathering like this wasn’t possible with traditional REST APIs which returned predefined sets of data.

Utilizing both technologies together allows developers to take advantage of all that Laravel has to offer. From Eloquent ORM and middleware, to authentication and more! Meanwhile driving it all forward with the flexibility and efficiency of GraphQL

 

Steps to Create and Test Laravel GraphQL API

1. Setting Up Your Laravel Project

This setup requires up and running Laravel environment. Now I have already installed Laravel along with all other required dependencies in the previous article. So I will use the same environment to demonstrate this topic.

First let's create a new project to create our GraphQL API:

sudo mkdir -p /opt/projects
sudo chmod 777 /opt/projects
cd /opt/projects
composer create-project --prefer-dist laravel/laravel laravelGraphqlApi

This command creates a new Laravel project named laravelGraphqlApi. Navigate inside your project:

cd laravelGraphqlApi

After creating your Laravel project, configure your .env file with your database connection details. I will fill these based on my environment but you should update these for your environment.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_laravel_db
DB_USERNAME=user1
DB_PASSWORD=Passw0rd

 

2. Create Models and Migration

When you're starting with a Laravel project, you'll typically generate both the model and its corresponding migration at the same time using Artisan commands. Laravel provides a convenient way to do this with a single command. The User model is usually created by default in a new Laravel project, but you may need to create the Post model and its migration.

Use Artisan to generate models for User and Post:

php artisan make:model User -m

This may return ERROR Model already exists. as in most cases a User Model and migration file is available for every project.

php artisan make:model Post -m
10 Simple Steps to Build & Test Laravel GraphQL API

Users Table: database/migrations/2014_10_12_000000_create_users_table.php

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

Posts Table: database/migrations/2024_02_21_173246_create_posts_table.php

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->string('title');
    $table->text('content');
    $table->timestamps();
});

Next to handle relationships and other GraphQL-related functionality, we will have to update User and Post Model files:

File: app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * Get the posts for the user.
     */
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

File: app/Models/Post.php

<?php

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'title',
        'content',
        'user_id',
    ];

    /**
     * Get the user that owns the post.
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Both models demonstrate how to define relationships in Laravel. The User model has a posts method that defines a one-to-many relationship with the Post model. Similarly, the Post model has a user method that defines an inverse one-to-many (belongs to) relationship with the User model.

Run the migrations. We use fresh to drop any existing table and perform a fresh migration:

php artisan migrate:fresh

This command executes the migration files in sequence, creating or modifying the database schema as defined.

10 Simple Steps to Build & Test Laravel GraphQL API

 

3. Install GraphQL Package

We can either use nuwave/lighthouse or rebing/graphql-laravel. You can choose either based on your preference for schema definition and project needs. Lighthouse is ideal for those who prefer using GraphQL SDL and directives for a concise, declarative approach, offering robust support for real-time data with subscriptions. Rebing suits developers seeking a flexible, Laravel-like coding experience, especially when handling multiple GraphQL schemas or requiring a traditional PHP setup.

We will use rebing/graphql-laravel for our tutorial and to install the same, run the following command in your Laravel project directory:

composer require rebing/graphql-laravel

After installation, you need to publish the package's configuration file to your Laravel project:

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

This command will create a graphql.php config file in your config directory.

10 Simple Steps to Build & Test Laravel GraphQL API

 

4. Implement GraphQL Schemas and Types

Creating GraphQL types in Laravel using the rebing/graphql-laravel package involves defining PHP classes that describe the structure of your GraphQL objects, including their fields and types.

If not already present, create a GraphQL directory within your app directory.

mkdir app/GraphQL

Inside the app/GraphQL directory, create a subdirectory called Types to store your type definitions.

mkdir app/GraphQL/Types

Next, you'll create PHP classes for each of your GraphQL types. Let's continue with the User and Post examples from before.

Open app/GraphQL/Types/UserType.php in your text editor or IDE, and define the class as follows:

<?php

namespace App\GraphQL\Types;

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

class UserType extends GraphQLType
{   
    protected $attributes = [
        'name' => 'User',
        'description' => 'A user',
        'model' => User::class, // Optionally bind the model to the type for automatic resolution
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'The id of the user',
            ],
            'name' => [
                'type' => Type::string(),
                'description' => 'The name of the user',
            ],
            'email' => [
                'type' => Type::string(),
                'description' => 'The email of the user',
            ],
            'posts' => [
                'type' => Type::listOf(GraphQL::type('Post')),
                'description' => 'The posts of the user',
            ],
        ];
    }
}

Similarly define app/GraphQL/Types/PostType.php:

<?php

namespace App\GraphQL\Types;

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

class PostType extends GraphQLType
{   
    protected $attributes = [
        'name' => 'Post',
        'description' => 'A post',
        'model' => Post::class,
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'The id of the post',
            ],
            'title' => [
                'type' => Type::string(),
                'description' => 'The title of the post',
            ],
            'content' => [
                'type' => Type::string(),
                'description' => 'The content of the post',
            ],
            'user' => [
                'type' => GraphQL::type('User'),
                'description' => 'The author of the post',
            ],
        ];
    }
}

 

5. Setting Up Queries and Mutations

Queries and mutations are two of the most important aspects in a GraphQL API. Queries let clients grab data from a server, specifying exactly what they need, while mutations give them the ability to update or change any information. This process involves setting up operations your API supports and implementing how these operations work within your database that you use as your data source.

What is our objective?

  • Defining types in GraphQL: Here, we need to define the types in our GraphQL schema. This usually means two things; the data types such as User and Post, and also the operation types (queries and mutations). For each type, there are fields that dictate how data can be queried or how it should be entered during a mutation.
  • Implementing resolvers: In this stage, we will implement resolvers for all queries and mutations. A resolver is a function that fetches data for a query or executes changes like those required by a mutation. In Laravel, we often use Eloquent models to retrieve or modify database data.
  • Registering operations: As soon as we have defined our types and implemented resolvers, we register these operations on our GraphQL schema. Thanks to this step, clients can call them up through the API endpoint of their GraphQL enabled app!

    Here is a structure of files which we created earlier and the files which we plan to create in this section:

    app/
    ├── GraphQL
    │   ├── Mutations
    │   │   ├── CreatePostMutation.php
    │   │   └── CreateUserMutation.php
    │   ├── Queries
    │   │   ├── PostsQuery.php
    │   │   └── UsersQuery.php
    │   └── Types
    │       ├── PostType.php
    │       └── UserType.php
    
    

    File: app/GraphQL/Queries/UsersQuery.php

    <?php
    
    namespace App\GraphQL\Queries;
    
    use GraphQL\Type\Definition\Type;
    use Rebing\GraphQL\Support\Query;
    use Rebing\GraphQL\Support\Facades\GraphQL;
    use App\Models\User;
    
    class UsersQuery extends Query
    {
        protected $attributes = [
            'name' => 'users',
        ];
    
        public function type(): Type
        {
            return Type::listOf(GraphQL::type('User'));
        }
    
        public function resolve($root, $args)
        {
            return User::all();
        }
    }

    File: app/GraphQL/Queries/PostsQuery.php

    <?php
    
    namespace App\GraphQL\Queries;
    
    use GraphQL\Type\Definition\Type;
    use Rebing\GraphQL\Support\Query;
    use Rebing\GraphQL\Support\Facades\GraphQL;
    use App\Models\Post;
    
    class PostsQuery extends Query
    {
        protected $attributes = [
            'name' => 'posts',
        ];
    
        public function type(): Type
        {
            return Type::listOf(GraphQL::type('Post'));
        }
    
        public function resolve($root, $args)
        {
            return Post::all();
        }
    }

    File: app/GraphQL/Mutations/CreateUserMutation.php

    <?php
    
    namespace App\GraphQL\Mutations;
    
    use GraphQL\Type\Definition\Type;
    use Rebing\GraphQL\Support\Mutation;
    use Rebing\GraphQL\Support\Facades\GraphQL;
    use App\Models\User;
    
    class CreateUserMutation extends Mutation
    {
        protected $attributes = [
            'name' => 'createUser',
        ];
    
        public function type(): Type
        {
            return GraphQL::type('User');
        }
    
        public function args(): array
        {
            return [
                'name' => [
                    'type' => Type::nonNull(Type::string()),
                ],
                'email' => [
                    'type' => Type::nonNull(Type::string()),
                ],
                'password' => [
                    'type' => Type::nonNull(Type::string()),
                ],
            ];
        }
    
        public function resolve($root, $args)
        {
            $user = new User();
            $user->fill([
                'name' => $args['name'],
                'email' => $args['email'],
                'password' => bcrypt($args['password']),
            ]);
            $user->save();
    
            return $user;
        }
    }

    File: app/GraphQL/Mutations/CreatePostMutation.php

    <?php
    
    namespace App\GraphQL\Mutations;
    
    use GraphQL\Type\Definition\Type;
    use Rebing\GraphQL\Support\Mutation;
    use Rebing\GraphQL\Support\Facades\GraphQL;
    use App\Models\Post;
    
    class CreatePostMutation extends Mutation
    {
        protected $attributes = [
            'name' => 'createPost',
        ];
    
        public function type(): Type
        {
            return GraphQL::type('Post');
        }
    
        public function args(): array
        {
            return [
                'title' => [
                    'type' => Type::nonNull(Type::string()),
                ],
                'content' => [
                    'type' => Type::nonNull(Type::string()),
                ],
                'user_id' => [
                    'type' => Type::nonNull(Type::int()),
                ],
            ];
        }
    
        public function resolve($root, $args)
        {
            $post = new Post();
            $post->fill([
                'title' => $args['title'],
                'content' => $args['content'],
                'user_id' => $args['user_id'],
            ]);
            $post->save();
    
            return $post;
        }
    }

     

    6. Registering Types, Queries and Mutations

    The schemas array you’ll be working with in the config/graphql.php configuration file is used to define the different arrays that your GraphQL server will use. Each one of these can have their own queries, mutations, types and middleware. Being able to do this is an important part of building a strong server. It allows you to expose your code to different clients based on what they are asking for and it makes maintaining multiple schemas easier too.

    Underneath the types key in a schema, you reference everything from object types all the way down to User and Post types. You do this in order to make sure everything lines up correctly so that each piece of code knows where it belongs in any query or mutation.

        'schemas' => [
            'default' => [
                'query' => [
                    'users' => App\GraphQL\Queries\UsersQuery::class,
                    'posts' => App\GraphQL\Queries\PostsQuery::class,
                ],
                'mutation' => [
                    'createUser' => App\GraphQL\Mutations\CreateUserMutation::class,
                    'createPost' => App\GraphQL\Mutations\CreatePostMutation::class,
                ],
                'types' => [
                    'User' => App\GraphQL\Types\UserType::class,
                    'Post' => App\GraphQL\Types\PostType::class,
                ],
            ],
        ],

    This structure ensures that your User and Post types are registered and available for use within your default schema, alongside the specific queries and mutations we've defined.

     

    7. Seed the Database (Optional)

    This is optional as if you wish to seed some data into the database to test the API then you can follow this section or skip to next section.

    Create factories for User and Post models:

    php artisan make:factory UserFactory --model=User
    php artisan make:factory PostFactory --model=Post
    

    Update the factory files in database/factories to generate dummy data. Laravel uses factories to create test data for your database, which is particularly useful for development and testing purposes. Below are examples of how to update these factory files for your User and Post models.

    For the User model, update the factory database/factories/UserFactory.php to generate users with names, email addresses, and passwords.

    <?php
    
    namespace Database\Factories;
    
    use Illuminate\Database\Eloquent\Factories\Factory;
    use Illuminate\Support\Facades\Hash;
    use Illuminate\Support\Str;
    use App\Models\User;
    
    class UserFactory extends Factory
    {
        /**
         * The name of the factory's corresponding model.
         *
         * @var string
         */
        protected $model = User::class;
    
        /**
         * Define the model's default state.
         *
         * @return array
         */
        public function definition()
        {
            return [
                'name' => $this->faker->name(),
                'email' => $this->faker->unique()->safeEmail(),
                'email_verified_at' => now(),
                'password' => bcrypt('password'), // Or use Hash::make('password')
                'remember_token' => Str::random(10),
            ];
        }
    }
    

    For the Post model, update the factory database/factories/PostFactory.php to generate posts with a title, content, and a user_id to associate each post with a user. Here's how the PostFactory might look:

    <?php
    
    namespace Database\Factories;
    
    use Illuminate\Database\Eloquent\Factories\Factory;
    use App\Models\Post;
    use App\Models\User;
    
    class PostFactory extends Factory
    {
        /**
         * The name of the factory's corresponding model.
         *
         * @var string
         */
        protected $model = Post::class;
    
        /**
         * Define the model's default state.
         *
         * @return array
         */
        public function definition()
        {
            return [
                'title' => $this->faker->sentence(),
                'content' => $this->faker->paragraph(),
                'user_id' => User::factory(), // Automatically creates a User for each Post
            ];
        }
    }
    

    To use these factories, you can now create below seeders and use the factories within them to populate your database.

    php artisan make:seeder UsersTableSeeder
    php artisan make:seeder PostsTableSeeder
    10 Simple Steps to Build & Test Laravel GraphQL API

    For instance, in our UsersTableSeeder.php, we will add:

    <?php
    
    namespace Database\Seeders;
    
    use Illuminate\Database\Console\Seeds\WithoutModelEvents;
    use Illuminate\Database\Seeder;
    use App\Models\User;
    
    class UsersTableSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         */
        public function run(): void
        {
            User::factory()->count(10)->create();
        }
    }

    And similarly, in our PostsTableSeeder.php

    <?php
    
    namespace Database\Seeders;
    
    use Illuminate\Database\Console\Seeds\WithoutModelEvents;
    use Illuminate\Database\Seeder;
    use App\Models\Post;
    
    class PostsTableSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         */
        public function run(): void
        {
            Post::factory()->count(50)->create();
        }
    }
    

    Next we need to call these seeders in your database/seeders/DatabaseSeeder.php

    <?php
    
    namespace Database\Seeders;
    
    // use Illuminate\Database\Console\Seeds\WithoutModelEvents;
    use Illuminate\Database\Seeder;
    
    class DatabaseSeeder extends Seeder
    {
        /**
         * Seed the application's database.
         */
        public function run(): void
        {
            // Call other seeders here
            $this->call([
                UsersTableSeeder::class,
                PostsTableSeeder::class,
            ]);
        }
    }
    

    Next execute below command to populate the database with users and posts. This setup will give you a good amount of dummy data to work with while developing and testing your GraphQL API.

    php artisan db:seed
    10 Simple Steps to Build & Test Laravel GraphQL API

    To view the content of your database, use Laravel Tinker:

    php artisan tinker
    

    Example commands in Tinker:

    \App\Models\User::all();
    \App\Models\Post::first();

    Sample Output:

    10 Simple Steps to Build & Test Laravel GraphQL API
    10 Simple Steps to Build & Test Laravel GraphQL API

     

    8. Setting Up GraphQL Routes

    The rebing/graphql-laravel package is an invaluable tool that lets you define GraphQL endpoints in your Laravel app. You’ll be sending your GraphQL queries and mutations through these routes, so it’s useful to get the hang of them early on.

    Typically, you’ll want to define these routes in either your routes/web.php or routes/api.php file. If you opt for the latter option, make sure to prefix them with /api.

    Here's a guide on how to define the GraphQL routes for your queries and mutations. Place it in routes/api.php:

    use Illuminate\Support\Facades\Route;
    use Rebing\GraphQL\Support\Facades\GraphQL;
    use Rebing\GraphQL\GraphQLController;
    
    Route::group(['prefix' => 'graphql'], function () {
        Route::post('/', [GraphQLController::class, 'query'])->name('graphql.execute');
        Route::get('/', [GraphQLController::class, 'query'])->name('graphql.execute');
    });

    In this setup, both GET and POST requests are directed to the same method (query). This is because, typically, a GraphQL server uses the same endpoint and method to handle all requests, distinguishing between queries and mutations based on the request payload, not the HTTP method.

     

    9. Running the Laravel Server

    To test your API with Postman Start your Laravel development server:

    php artisan serve
    

     

    10. Testing GraphQL API

    To test your GraphQL API with Postman, follow the steps below using this walk through as a reference:

    POST Request

    • Update the request type to POST: In Postman, create a new request and change its type to POST.
    • Put in the URL: Use the URL of your GraphQL endpoint (e.g., http://localhost:8000/graphql if you're running your Laravel app locally).
    • Set Headers: Add a header and set Content-Type to application/json.
    • Configure the Body: Select 'Body' tab, then pick 'raw', and finally select 'JSON' as the format. Write your GraphQL query or mutation in the body as shown below for example’s sake:
    10 Simple Steps to Build & Test Laravel GraphQL API

    GET Request

    Testing with a GET request is quite different because you have to pass the query as a URL parameter:

    1. Change the type of request to GET: Create a new request in Postman and switch the type to GET.
    2. Fill in the URL with Query Parameters: To append your query directly to the URL, you will have to encode it. For example:
    http://localhost:8000/graphql?query={users{id,name,email,posts{id,title}}}
    
    10 Simple Steps to Build & Test Laravel GraphQL API

    So with this we have successfully built and tested Laravel GraphQL API Server by sending POST and GET Requests.

     

    Steve Alila

    Steve Alila

    He specializes in web design, WordPress development, and data analysis, with proficiency in Python, JavaScript, and data extraction tools. Additionally, he excels in web API development, AI integration, and data presentation using Matplotlib and Plotly. You can connect with him on his LinkedIn profile.

    Can't find what you're searching for? Let us assist you.

    Enter your query below, and we'll provide instant results tailored to your needs.

    If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

    Buy GoLinuxCloud a Coffee

    For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

    Thank You for your support!!

    Leave a Comment