How to Build a GraphQL Server with Laravel GraphQL and Test It with Postman

GraphQL is a powerful query language developed by Facebook that offers a more efficient and flexible alternative to traditional REST APIs. With GraphQL, clients can request the exact data they need in a single request, reducing over-fetching and under-fetching of data. This results in faster performance and easier data management for applications.

In this tutorial, we‘ll explore how to build a GraphQL server using the Laravel PHP framework and the Laravel GraphQL package. We‘ll implement queries and mutations to fetch and create data. Finally, we‘ll use Postman to test our GraphQL API endpoints.

Setting Up the Project

First, make sure you have PHP and Composer installed on your system. Then open your terminal and run the following command to create a new Laravel project:

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

Navigate into the project directory:

cd laravel-graphql

Next, we‘ll install the Laravel GraphQL package using Composer:

composer require rebing/graphql-laravel

After the installation is complete, publish the package configuration file:

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

This will create a graphql.php file in your config directory where you can define your GraphQL schema.

Configuring the GraphQL Schema

In GraphQL, the schema defines the structure of your API, including the available types, queries, and mutations. Open the config/graphql.php file and update the schema configuration:

‘schemas‘ => [
    ‘default‘ => [
        ‘query‘ => [
            // Add query classes here
        ],
        ‘mutation‘ => [
            // Add mutation classes here
        ],
        ‘types‘ => [
            // Add type classes here    
        ],
    ],
],

We‘ll come back to this file later to register our GraphQL queries, mutations, and types.

Creating Models and Migrations

For this tutorial, let‘s assume we‘re building a simple blog application with users and posts. We‘ll create the necessary models and migrations.

First, generate the User model if it doesn‘t already exist:

php artisan make:model User -m

Open the created migration file in the database/migrations directory and define the user table schema:

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

Next, create the Post model and migration:

php artisan make:model Post -m

Open the post migration file and define the table schema:

Schema::create(‘posts‘, function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger(‘user_id‘);
    $table->string(‘title‘);
    $table->text(‘content‘);
    $table->timestamps();

    $table->foreign(‘user_id‘)->references(‘id‘)->on(‘users‘)->onDelete(‘cascade‘);
});

The posts table includes a user_id foreign key to establish the relationship between posts and users.

Run the migrations to create the tables in your database:

php artisan migrate

Defining GraphQL Types

GraphQL uses types to define the structure of the data. Let‘s create GraphQL type classes for our User and Post models.

Create a new directory app/GraphQL/Types and add a UserType class:

namespace App\GraphQL\Types;

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

class UserType extends GraphQLType
{
    protected $attributes = [
        ‘name‘ => ‘User‘,
        ‘description‘ => ‘A user‘,
        ‘model‘ => User::class,
    ];

    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‘,
            ],
            ‘created_at‘ => [
                ‘type‘ => Type::string(),
                ‘description‘ => ‘The date the user was created‘,
            ],
            ‘updated_at‘ => [
                ‘type‘ => Type::string(),
                ‘description‘ => ‘The date the user was last updated‘,
            ],
        ];
    }
}

The UserType class defines the fields available for the User model, including the ID, name, email, created_at, and updated_at fields.

Similarly, create a PostType class in the same directory:

namespace App\GraphQL\Types;

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

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‘ => Type::nonNull(GraphQL::type(‘User‘)),
                ‘description‘ => ‘The user who created the post‘,
            ],
            ‘created_at‘ => [
                ‘type‘ => Type::string(),
                ‘description‘ => ‘The date the post was created‘,
            ],
            ‘updated_at‘ => [
                ‘type‘ => Type::string(),
                ‘description‘ => ‘The date the post was last updated‘,
            ],
        ];
    }
}

The PostType class defines the fields for the Post model, including a relationship to the User model.

Defining GraphQL Queries

Now, let‘s create GraphQL query classes to fetch users and posts from the database.

Create a new directory app/GraphQL/Queries and add a UsersQuery class:

namespace App\GraphQL\Queries;

use App\Models\User;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Query;

class UsersQuery extends Query
{
    protected $attributes = [
        ‘name‘ => ‘users‘,
        ‘description‘ => ‘A query to fetch users‘,
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type(‘User‘));
    }

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

The UsersQuery class defines a query to fetch all users from the database. The type method specifies that the query returns a list of User types. The resolve method is responsible for fetching the data, in this case, using the User model‘s all method.

Similarly, create a PostsQuery class:

namespace App\GraphQL\Queries;

use App\Models\Post;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Query;

class PostsQuery extends Query
{
    protected $attributes = [
        ‘name‘ => ‘posts‘,
        ‘description‘ => ‘A query to fetch posts‘,
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type(‘Post‘));
    }

    public function resolve($root, $args)
    {
        return Post::with(‘user‘)->get();
    }
}

The PostsQuery class defines a query to fetch all posts along with their associated users using the with method for eager loading.

Defining GraphQL Mutations

Mutations in GraphQL allow us to modify data on the server. Let‘s create a mutation to create new users.

Create a new directory app/GraphQL/Mutations and add a CreateUserMutation class:

namespace App\GraphQL\Mutations;

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

class CreateUserMutation extends Mutation
{
    protected $attributes = [
        ‘name‘ => ‘createUser‘,
        ‘description‘ => ‘A mutation to create a new user‘,
    ];

    public function type(): Type
    {
        return GraphQL::type(‘User‘);
    }

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

    public function resolve($root, $args)
    {
        return User::create([
            ‘name‘ => $args[‘name‘],
            ‘email‘ => $args[‘email‘],
            ‘password‘ => bcrypt($args[‘password‘]),
        ]);
    }
}

The CreateUserMutation class defines a mutation to create a new user. The args method specifies the required input fields for creating a user (name, email, and password). The resolve method creates a new user using the User model‘s create method and the provided input arguments.

Registering GraphQL Schema

With our types, queries, and mutations defined, it‘s time to register them in the GraphQL schema configuration.

Open the config/graphql.php file and update the schema section:

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

We‘ve registered our queries (UsersQuery and PostsQuery) under the query key, our mutation (CreateUserMutation) under the mutation key, and our types (UserType and PostType) under the types key.

Testing with Postman

Now that our GraphQL server is set up, let‘s test it using Postman.

Start your Laravel development server:

php artisan serve

Open Postman and create a new POST request to http://localhost:8000/graphql.

In the request body, select the GraphQL option and enter the following query to fetch all users:

query {
  users {
    id
    name
    email
    created_at
    updated_at
  }
}

Send the request, and you should receive a response with the list of users in JSON format.

Next, let‘s test the posts query with the associated users:

query {
  posts {
    id
    title
    content
    user {
      id
      name
    }
    created_at
    updated_at
  }
}

This query fetches all posts along with the associated user‘s ID and name.

Finally, let‘s test the createUser mutation:

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

This mutation creates a new user with the provided name, email, and password and returns the created user‘s details.

Troubleshooting Common Errors

One common error you might encounter while building a GraphQL server with Laravel is "schema must contain uniquely named types but contains multiple types named".

This error occurs when you have duplicate type names in your schema. To resolve this issue:

  1. Double-check your type class names and ensure they are unique across your schema.
  2. If you have multiple schemas defined, make sure each schema has uniquely named types.
  3. Verify that you haven‘t accidentally registered the same type multiple times in your schema configuration.

By following these steps and ensuring unique type names, you should be able to resolve the "schema must contain uniquely named types but contains multiple types named" error.

Best Practices and Additional Resources

Here are some best practices to keep in mind when building GraphQL servers with Laravel:

  1. Use meaningful and descriptive names for your types, fields, queries, and mutations to improve the readability and maintainability of your schema.
  2. Leverage GraphQL‘s type system to enforce data validation and provide clear error messages to clients.
  3. Use pagination for large datasets to avoid performance issues and improve response times.
  4. Implement proper authentication and authorization mechanisms to secure your GraphQL API.
  5. Cache frequently accessed data to optimize performance and reduce database queries.

For more in-depth learning and exploration of GraphQL with Laravel, check out these additional resources:

Conclusion

In this tutorial, we explored how to build a GraphQL server using Laravel and the Laravel GraphQL package. We defined our schema, types, queries, and mutations, and tested our API endpoints using Postman. We also discussed common errors like "schema must contain uniquely named types but contains multiple types named" and provided troubleshooting tips.

GraphQL provides a powerful and flexible approach to building APIs, and Laravel makes it easy to integrate GraphQL into your projects. By following best practices and leveraging the available resources, you can create robust and efficient GraphQL servers with Laravel.

Remember to keep learning, exploring, and experimenting with GraphQL and Laravel to unleash their full potential in your web development journey.

Similar Posts