Getting started with GraphQL in Laravel

graphql-laravel-header.png

This short tutorial uses GraphQL, Laravel and SQLite to build a to-do list. It introduces GraphQL, and quickly gets you up and running

Introduction

Since it was publicly released in 2015 by Facebook, companies like GitHub and Pinterest have started using GraphQL in production. GraphQL introduces lots of improvements over REST. There is this misconception that GraphQL is all about JavaScript. Many different programming languages support GraphQL. So in this tutorial, I will help correct that misconception by showing you how to get started with GraphQL in Laravel.

Prerequisites

This tutorial assumes the following:

  • Basic knowledge of GraphQL
  • Basic knowledge of Laravel

Also, ensure you have the following installed:

  • PHP
  • SQLite
  • Composer
  • Laravel installer

What we’ll be building

We’ll be using the concept of a to-do list.

Create a new Laravel app

We’ll start by creating a new Laravel project. I’ll be using the Laravel installer:

1$ laravel new laravel-graphql-tasks

To use GraphQL is our Laravel application, we’ll be making use of a package called laravel-graphql. Let’s install the package:

1$ cd laravel-graphql-tasks
2    $ composer require folklore/graphql

Note: The rest of the tutorial assumes you already in the laravel-graphql-tasks directory

Once that’s done installing, let’s run the command below:

1$ php artisan vendor:publish --provider="Folklore\GraphQL\ServiceProvider"

This will create two new files: config/graphql.php which is the package configuration file, and resources/views/vendor/graphql/graphiql.php which will allow us test our application using GraphiQL.

Create a task model

Let’s create a Task model and its corresponding migration file. To do that, run the command below:

1$ php artisan make:model Task -m

Once that’s done, open the migration file that was generated and update the up method as below:

1// database/migrations/TIMESTAMP_create_tasks_table.php
2
3    public function up()
4    {
5      Schema::create('tasks', function (Blueprint $table) {
6        $table->increments('id');
7        $table->string('title');
8        $table->boolean('is_completed')->default(0);
9        $table->timestamps();
10      });
11    }

We add basic columns to the tasks table. We set the is_completed column to false by default.

Next, we need to run the migrations. But before we can do that, we need to set up our database. For the purpose of this tutorial, we’ll be using SQLite. So, create a database.sqlite file inside the database directory. Lastly, let’s update the .env file as below:

1// .env
2
3    DB_CONNECTION=sqlite
4    DB_DATABASE=FULL_PATH_TO_DATABASE_SQLITE

Now, let’s run the migration:

1$ php artisan migrate

Database seeder

So as to have some data to work with, let’s create a database seeder to populate the tasks table with some data. Run the command below:

1$ php artisan make:seeder TasksTableSeeder

Then, within the database/factories directory, create a new TaskFactory.php file and paste the code below in it:

1// database/factories/TaskFactory.php
2
3    <?php
4
5    use Faker\Generator as Faker;
6
7    $factory->define(App\Task::class, function (Faker $faker) {
8        return [
9            'title' => $faker->sentence,
10        ];
11    });

We only specify the value of the title column since we already defined the is_completed column to be false by default.

Next, update the run method of database/seeds/TasksTableSeeder.php as below:

1// database/seeds/TasksTableSeeder.php
2
3    public function run()
4    {
5      factory(App\Task::class, 5)->create();
6    }

Now, we can run the database seeder:

1$ php artisan db:seed --class=TasksTableSeeder

Defining schemas

With the setup out of the way, let’s start defining the schemas for our to-do list. Schemas describe how data are shaped and what data on the server can be queried. Schemas can be of two types: Query and Mutation. Our schemas can be define inside config/graphql.php. We’ll update this file shortly.

Creating GraphQL query

Our to-do list will have just one query, which will be to fetch all the tasks that have been added to the GraphQL server. Before we can create a query, we need to first define a type. So, we’ll define a Task type. To do this, create a new GraphQL directory within the app directory. Within the GraphQL directory, create a new Type directory. Within app/GraphQL/Type directory, create a new TaskType.php file and paste the following code in it:

1// app/GraphQL/Type/TaskType.php
2
3    <?php
4
5    namespace App\GraphQL\Type;
6
7    use GraphQL\Type\Definition\Type;
8    use Folklore\GraphQL\Support\Type as GraphQLType;
9
10    class TaskType extends GraphQLType
11    {
12      protected $attributes = [
13        'name' => 'Task',
14        'description' => 'A task'
15      ];
16
17      public function fields()
18      {
19        return [
20          'id' => [
21            'type' => Type::nonNull(Type::int()),
22            'description' => 'The id of a task'
23          ],
24          'title' => [
25            'type' => Type::string(),
26            'description' => 'The title of a task'
27          ],
28          'is_completed' => [
29            'type' => Type::boolean(),
30            'description' => 'The status of a task'
31          ],
32        ];
33      }
34    }

We give the type a name and description. Also, we define the fields (id, title, is_completed) that the Task type will have.

Now, let’s create the query. Create a new Query directory inside app/GraphQL, and within the Query directory, create a new TasksQuery.php file and paste the following code in it:

1// app/GraphQL/Query/TasksQuery.php
2
3    <?php
4
5    namespace App\GraphQL\Query;
6
7    use GraphQL;
8    use GraphQL\Type\Definition\Type;
9    use Folklore\GraphQL\Support\Query;
10    use App\Task;
11
12    class TasksQuery extends Query
13    {
14      protected $attributes = [
15        'name' => 'tasks'
16      ];
17
18      public function type()
19      {
20        return Type::listOf(GraphQL::type('Task'));
21      }
22
23      public function resolve($root, $args)
24      {
25        return Task::all();
26      }
27    }

We give the query a name of tasks, then we define the query type (which is the Task type we defined above). Since we want the query to fetch all the tasks that have been created, we use the listOf type on the Task type, which will return an array of tasks. Lastly, we define a resolve method which will handle the actual fetching of the tasks. We are simply returning all the tasks from the database.

With our type and query defined, let’s add them to config/graphql.php:

1// config/graphql.php
2
3    'schemas' => [
4      'default' => [
5        'query' => [
6          'tasks' => \App\GraphQL\Query\TasksQuery::class,
7        ],
8        // ...
9      ]
10    ],
11
12    'types' => [
13      'Task' => \App\GraphQL\Type\TaskType::class,
14    ],

Creating GraphQL mutations

Mutations are used to perform write operations. We’ll create two mutations, which will be used to add a new task and update the status of a task respectively. Create a new Mutation directory within app/GraphQL, and within Mutation, create a new NewTaskMutation.php file and paste the following code into it:

1// app/GraphQL/Mutation/NewTaskMutation.php
2
3    <?php
4
5    namespace App\GraphQL\Mutation;
6
7    use GraphQL;
8    use GraphQL\Type\Definition\Type;
9    use Folklore\GraphQL\Support\Mutation;
10    use App\Task;
11
12    class NewTaskMutation extends Mutation
13    {
14      protected $attributes = [
15        'name' => 'newTask'
16      ];
17
18      public function type()
19      {
20        return GraphQL::type('Task');
21      }
22
23      public function args()
24      {
25        return [
26          'title' => [
27            'name' => 'title',
28            'type' => Type::nonNull(Type::string())
29          ]
30        ];
31      }
32
33      public function resolve($root, $args)
34      {
35        $task = new Task();
36
37        $task->title = $args['title'];
38        $task->save();
39
40        return Task::find($task->id);
41      }
42    }

We give the mutation a name of newTask, then we define the type this mutation will return. We define an args method that returns an array of arguments that the mutation can accept. The mutation will accept just one argument, which is the title of a task. Lastly, we define a resolve method that does the actual insertion of the new task into the database and returns the newly created task.

What if we want to validate the data before adding a new task? Well, it is possible to add validation rules to a mutation. The laravel-graphql package uses the laravel Validator to performs validation against the args. There are two ways to add validation to mutations: we can define a rules method and return an array containing the rules for each argument, or we define the rules directly while defining an argument. We’ll be opting for the second way.

Update the args method as below:

1// app/GraphQL/Mutation/NewTaskMutation.php
2
3    public function args()
4    {
5      return [
6        'title' => [
7          'name' => 'title',
8          'type' => Type::nonNull(Type::string()),
9          'rules' => ['required'],
10        ]
11      ];
12    }

We add a rule that makes the title argument required.

Now, let’s create the mutation to update the status of a task. Create a new UpdateTaskStatusMutation.php file within app/GraphQL/Mutation and paste the following code into it:

1// app/GraphQL/Mutation/UpdateTaskStatusMutation.php
2
3    <?php
4
5    namespace App\GraphQL\Mutation;
6
7    use GraphQL;
8    use GraphQL\Type\Definition\Type;
9    use Folklore\GraphQL\Support\Mutation;
10    use App\Task;
11
12    class UpdateTaskStatusMutation extends Mutation
13    {
14      protected $attributes = [
15        'name' => 'updateTaskStatus'
16      ];
17
18      public function type()
19      {
20        return GraphQL::type('Task');
21      }
22
23      public function args()
24      {
25        return [
26          'id' => [
27            'name' => 'id',
28            'type' => Type::nonNull(Type::int()),
29            'rules' => ['required'],
30          ],
31          'status' => [
32            'name' => 'status',
33            'type' => Type::nonNull(Type::boolean()),
34            'rules' => ['required'],
35          ]
36        ];
37      }
38
39      public function resolve($root, $args)
40      {
41        $task = Task::find($args['id']);
42
43        if (!$task) {
44          return null;
45        }
46
47        $task->is_completed = $args['status'];
48        $task->save();
49
50        return $task;
51      }
52    }

This is quite similar to the NewTaskMutation. It accepts two arguments: the ID of the task to update and the status of the task. The resolve method gets the task with the supplied ID from the database. If there is no task with the supplied ID, we simply return null. Otherwise, we update the task with the supplied status and persist to the database. Finally, we return the update the task.

To wrap up the mutations, let’s add them to config/graphql.php:

1// config/graphql.php
2
3    'schemas' => [
4      'default' => [
5        // ...
6        'mutation' => [
7          'newTask' => \App\GraphQL\Mutation\NewTaskMutation::class,
8          'updateTaskStatus' => \App\GraphQL\Mutation\UpdateTaskStatusMutation::class,
9        ]
10      ]
11    ],

Testing with GraphiQL

So far, we have created a GraphQL server with one query and two mutations. Let’s now see how we test out the server using GraphiQL. Recall that when we published the laravel-graphql package files, a resources/views/vendor/graphql/graphiql.php was also created. This will allow us to test our GraphQL server within our Laravel app.

Get the app running with the command below:

1$ php artisan serve

This will get the app running on http://127.0.0.1:8000, then visit http://127.0.0.1:8000/graphiql in your browser. GraphiQL should now be running as in the image below:

App preview

We can see our GraphQL server in action:

Test GraphQL screenshot

Conclusion

In this tutorial, we have seen how to use GraphQL in a Laravel application. We’ve only scratched the surface of working with GraphQL in Laravel applications. In my next tutorial, we’ll dive deeper as we’ll be building an API with GraphQL and Laravel. So stay tuned.

The complete code for this tutorial is available on GitHub.