Build a GraphQL Powered API with Laravel

July 31, 2020
Written by
Oluyemi Olususi
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Build a GraphQL Powered API with Laravel

Often referred to as a platform or programming language agnostic query language, GraphQL is a comprehensive approach to guide software engineers in building data-driven applications. It is, in my opinion, the new API standard, offering client applications the flexibility to request exact data and not an entire record from the database. If you are not yet conversant with this technology or looking for simplified technical content to guide you through, you are in the right place.

In this tutorial, you will learn some of the fundamental concepts required to build a Laravel API powered by GraphQL. This is a shift from the conventional architecture of building REST APIs, as we will take a different approach in this tutorial. To help us explore GraphQL, we will build a bookstore API with an ability to carry out CRUD ( Create, Read, Update, Delete ) operations on data, in your database.

Prerequisites

You should have basic knowledge of building web applications with Laravel to easily complete this tutorial. Lastly, you need to installComposer globally on your computer to manage dependencies.

Creating a New Laravel Project

To begin, we will create a new Laravel application using Composer, by running the following command from the terminal:

$ composer create-project --prefer-dist laravel/laravel lara-bookstore

The preceding command will create a Laravel project in a new directory named lara-bookstore in your local development folder, or whenever you ran the command from. Go to the project folder and run the Laravel project to interact with the default functionality, using the following command:

// Move into project
$ cd lara-bookstore

// Run the application
$ php artisan serve

Laravel default page

Install GraphQL Laravel library

Several GraphQL libraries have been created by developers to make working with GraphQL easy within a Laravel project. Here, we will use the Laravel GraphQL library (rebing/graphql-laravel) Stop the application from running using CTRL + C, hit Enter, and then issue the following command instructing Composer to install the library:

// Install library
$ composer require rebing/graphql-laravel

Once the installation is completed, publish a copy of the configuration file from the vendor folder using this command:

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

You will see the following output:

Copied File [/vendor/rebing/graphql-laravel/config/config.php] To [/config/graphql.php]
Publishing complete.

This will copy the configuration file into config.graphql.php. We will return to modify its content later in the tutorial.

Creating Book Model

In this section, we will simultaneously create a Book model and generate a migration file. Issue the following command for that purpose:

$ php artisan make:model Book -m

Next, the migration file will be used to create the Books table and its corresponding fields. Modify the newly generated migration file by replacing its content with the following:

<?php

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

class CreateBooksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('author');
            $table->string('language');
            $table->string('year_published');
            $table->string('isbn');
            $table->timestamps();
        });
    }

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

The Books table will have title, author, language, year_published, and isbn fields amongst others.

Now, add these fields as $fillable properties for the Book model. Open app/Book.php and replace its content with:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    protected $fillable = [
        'title', 'author', 'language', 'year_published', 'isbn'
    ];
}

Creating a Seeder File for the Books Table

Let’s create some initial data to populate our database tables using the Seeder class in Laravel. To generate a seeder file specifically for the Book table, type the following command:

$ php artisan make:seeder BookSeeder

This will generate a new file named BookSeeder.php within the database/seeds folder. Open the newly created file and populate it with the following:

<?php

use Illuminate\Database\Seeder;
use App\Book;

class BookSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Book::insert([
            [
                'title' => "Half of a Yellow Sun",
                'author' => "Chimamanda Ngozi Adichie",
                'language' => "English",
                'year_published' => "2006",
                'isbn' => "978-0-00-720028-3"
            ],
            [
                'title' => "Encyclopedia of Pseudoscience:",
                'author' => "William F. Williams",
                'language' => "English",
                'year_published' => "2000",
                'isbn' => "978-1-57958-207-4"
            ],
            [
                'title' => "Things fall apart",
                'author' => "Chinua Achebe",
                'language' => "English",
                'year_published' => "1958",
                'isbn' => "0385474547"
            ],
            [
                'title' => "Tall Tales about the Mind and Brain",
                'author' => "Della Sala",
                'language' => "English",
                'year_published' => "2007",
                'isbn' => "0-19-856877-0"
            ],
            [
                'title' => "Anthills of the Savannah",
                'author' => "Chinua Achebe",
                'language' => "English",
                'year_published' => "1987",
                'isbn' => "0385260458"
            ],
            [
                'title' => "Americanah",
                'author' => "Chimamanda Ngozi Adichie",
                'language' => "English",
                'year_published' => "2013",
                'isbn' => "978-0-307-96212-6"
            ],
        ]);
    }
}

Here, we created an instance of the Book model and inserted an array of data containing the list of books with a title, author, language, year_published, and isbn respectively. For these columns to be seeded with the data, we need the BookSeeder from the default DatabaseSeeder class. Open database/seed/DatabaseSeeder.php file and update it with:

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
         $this->call(BookSeeder::class); // add this line
    }
}

Create a Database and Set Up a Database Connection

We are all set to create a database table and insert some dummy data into it. Before you proceed, ensure that you have a database created locally and update the .env file with your database credentials as shown below:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=YOUR_DB_NAME
DB_USERNAME=YOUR_DB_USER
DB_PASSWORD=YOUR_DB_PASSWORD

NOTE: Replace the YOUR_DB_NAME, YOUR_DB_USER, and YOUR_DB_PASSWORD placeholders with the appropriate details of your database.

You can now run the migration to create the database schema:

$ php artisan migrate --seed

The included --seed flag will ensure that the database seeder is run after the database schema has been created.

Now that we have an application with the appropriate data for testing the functionality of our API, we will proceed to set up the GraphQL server.

Building GraphQL Server

Earlier, we installed the Laravel GraphQL library that will be used for this application. In this section, we will start by defining the GraphQL schema. Schema provides the exact description of the fields in the database and defines how the created data will be retrieved through GraphQL.

Begin by creating a GraphQL folder in the app folder of your application. Within this folder, create three new subfolders namely:

  • Mutations: This folder will house classes that will be used to carry out the insert, update, and delete operations.
  • Queries: GraphQL queries will be used to fetch data from the database and the classes for that functionality will be defined here.
  • Types: Types are objects that represent the kind of objects that can be retrieved from the database. The BookType class will be housed here.

In the next section, we will start creating the necessary classes within the folders above

Creating a GraphQL Type for the Application

Create a new file BookType.php within the app/GraphQL/Types folder and add the following code:

<?php

namespace App\GraphQL\Types;

use App\Book;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;

class BookType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Book',
        'description' => 'Collection of books and details of Author',
        'model' => Book::class
    ];


    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'Id of a particular book',
            ],
            'title' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The title of the book',
            ],
            'author' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'Name of the author of the book',
            ],
            'language' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The language which the book was written in',
            ],
            'year_published' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The year the book was published',
            ],
            'isbn' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The international standard book number for the book',
            ]
        ];
    }
}

You will notice we created an $attributes array that defines the name, a brief description, and the model linked, with the type defined here. Lastly, we defined a function that returns the properties of the fields as an array.

Creating GraphQL Queries

As stated before, queries will be used to retrieve data from the database. In this case, we will create queries to fetch a particular book and another to retrieve the details of all the books in the database.

The Book Query

Let’s create a file with the name BookQuery.php within the app/GraphQL/Queries folder and paste the following code in it:

<?php

namespace App\GraphQL\Queries;

use App\Book;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class BookQuery extends Query
{
    protected $attributes = [
        'name' => 'book',
    ];

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

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

    public function resolve($root, $args)
    {
        return Book::findOrFail($args['id']);
    }
}

Here, we defined the query name and referenced the type Book that was created earlier as the GraphQL type to be used. The function args() returns the unique identifier that will be used for a particular book, which is an id in this case. Once resolved, the query will search for the book using the identifier and return its details.

The Books Query

Unlike the class defined in the previous section, here we will define a query to retrieve a collection of books. Navigate into the app/GraphQL/Queries folder and create a file with the name BooksQuery.php. Use the following content for it:

<?php

namespace App\graphql\Queries;

use App\Book;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class BooksQuery extends Query
{
    protected $attributes = [
        'name' => 'books',
    ];

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

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

Creating the Mutation

Any query implemented that causes data to be written or altered is often sent through mutation. Mutations can be created to insert, update, and delete a record in the database. Let’s start by defining one to create a new record for a book.

Create a Book

Create a new file within the app/GraphQL/Mutations folder and name it CreateBookMutation.php. Open it and paste the following content in it:

<?php

namespace App\graphql\Mutations;

use App\Book;
use Rebing\GraphQL\Support\Mutation;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;

class CreateBookMutation extends Mutation
{
    protected $attributes = [
        'name' => 'createBook'
    ];

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

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

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

        return $book;
    }
}

It is important to note the name of the mutation as it makes it easy to reference this class later. In addition to defining the GraphQL type, we also returned the properties of each field before finally creating an instance of the Book model and filling it with the arguments passed along with the query.

Update a Particular Book

To define a mutation to update the records of a particular book, create a new file within the app/GraphQL/Mutations folder and name it UpdateBookMutation.php. Open it and paste the following content in it:

<?php

namespace App\graphql\Mutations;

use App\Book;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;

class UpdateBookMutation extends Mutation
{
    protected $attributes = [
        'name' => 'updateBook'
    ];

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

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

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

        return $book;
    }
}

Similar to what we had in the previous section, we created an attribute that specified the name of the query and created the field properties for the query arguments. Finally, we used the unique id to retrieve the book of interest and update its details.

Delete a Book

The last mutation to be created is for deleting a specific book. Create a new file within the app/GraphQL/Mutations folder and name it DeleteBookMutation.php. Open it and paste the following content in it:

<?php

namespace App\graphql\Mutations;

use App\Book;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;

class DeleteBookMutation extends Mutation
{
    protected $attributes = [
        'name' => 'deleteBook',
        'description' => 'Delete a book'
    ];

    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)
    {
        $book = Book::findOrFail($args['id']);

        return  $book->delete() ? true : false;
    }
}

Registering the Schema and Type

After creating all of the schemas, they need to be registered within the configuration file of the GraphQL library used for this project. To do that, open the config/graphql.php file, locate the schemas, and update as shown here. Do the same for the types property as well:

return [
...
'schemas' => [
    'default' => [
        'query' => [
            'book' => App\GraphQL\Queries\BookQuery::class,
            'books' => App\GraphQL\Queries\BooksQuery::class,
        ],
        'mutation' => [
            // Create a book
            'createBook' => App\GraphQL\Mutations\CreateBookMutation::class,
            // update book
            'updateBook' => App\GraphQL\Mutations\UpdateBookMutation::class,
            // delete a book
            'deleteBook' => App\GraphQL\Mutations\DeleteBookMutation::class,
        ],
        'middleware' => [],
        'method' => ['get', 'post'],
    ],
],
'types' => [
    'Book' => App\GraphQL\Types\BookType::class,
],
...
]

Run the Application

Start the application with php artisan serve. This will start the local PHP server and run the Laravel application at http://localhost:8000.

Installing GraphiQL IDE

To test the application, we need to install a tool that can be used for testing GraphQL endpoints. One of the popular tools in the tech community created for this purpose isGraphiQL. It is a GUI for editing and testing GraphQL queries and mutations. Click here to download a version suitable for your operating system.

Once you are done, open it and use http://localhost:8000/graphql as the GraphQL Endpoint.

Testing the Endpoints

Return all books

Retrieve the list of books by running the query below in the query window:

{
  books {
    id
    title,
    author
  }
}

Collections of books

This will return the collection of books seeded into the database earlier.

Return a specific book

Next, to fetch the details of a particular book, use this query:

{
  book(id: 6) {
    title,
    author
  }
}

Retrieve a book

Notice that we had to pass an argument that represents the unique identifier for the specific book.

Create a book

Here, we will start with mutations by using the following to create a book:

mutation createBook {
  createBook(title: "John Doe book", author: "johndoe@test.com", 
    language: "secret", year_published: "2020", isbn: "635353353") {
    id
    title
    author
  }
}

Create a book

Update a Book

And to update the details of a specific book:

mutation updateBook {
  updateBook(id: 2, title: "Mine", author: "sample book", language: "secret", year_published: "2020", isbn: "635353353") {
    id
    title
    author
  }
}

Update a book details

Conclusion

GraphQL is a game-changer and a great addition to the software development community. It is gaining more popularity because it is robust enough for any kind of application, irrespective of the size.

In this tutorial, we were able to conveniently build a Laravel backend API powered by GraphQL. We also set things up fundamentally and created a complete CRUD operation, which is similar to what is obtainable for any web resources.

Check this repository for the complete source code of the application we built and don’t hesitate to explore and build upon it. Looking forward to what you are going to build with the knowledge acquired from this tutorial. Happy coding!

Olususi Oluyemi is a tech enthusiast, programming freak, and a web development junkie who loves to embrace new technology.