If you're looking for a basic tutorial for using GraphQL with Laravel, you came to the right place. GraphQL has become a really helpful tool for us at Foster Made when we're developing Laravel applications. In my opinion, it provides a powerful and interesting concept that responds to challenges caused by the growing needs of better approaches for API development and a natural adaptation to advances in technology.
Many of the advancements in technology today can be credited to major improvements in API architecture styles such as REST (Representational State Transfer). For the past decade, people have mostly been familiar with REST based APIs, but since the rise of APIs in the past years, newer challenges have risen. One major problem with REST APIs is that retrieving the data you want often requires multiple calls. An additional problem with REST APIs is their ability to adapt to multiple platforms. In 2012, Facebook started developing technology that goes by the name of “GraphQL” to help alleviate some of those challenges. The company then released it to the public in 2015.
GraphQL addresses known issues with REST by providing more flexibility to API clients to request only the data that they need at any given point in time while structuring it exactly the way it is intended to be used. In a nutshell, GraphQL is a platform-agnostic query language that describes how to ask for data and is generally used to load data from a server to a client. GraphQL comes with 3 main features: it allows the API client to specify what data it needs to consume, it facilitates the aggregation of data across multiple resources and nested resources, and it uses a type system to describe data.
Before we begin, let's assume you have a basic knowledge of the Laravel framework. Let’s also assume that you have been through the official GraphQL website and went across this documentation. You should also ensure that you have PHP, SQLite, Composer as well as the Laravel installer already setup on your computer.
First we begin by creating a Laravel app using the Laravel installer:
$ laravel new testapp
In order to use GraphQL in this project, we will also import the “rebing/graphql-laravel” package via Composer.
$ cd testapp
$ composer require rebing/graphql-laravel
Once the package has been installed, we will need to copy the GraphQL package’s configuration files from the vendor directory to the application
$ php artisan vendor:publish
Select the following providers from the list of possible providers by typing its corresponding number:
Provider: Rebing\GraphQL\GraphQLServiceProvider
This will create the file: config/graphql.php which is the package configuration file.
Open that file and edit it as follows:
'params_key' => 'variables',
In order to easily test our GraphQL App, we will also install GraphiQL ( An in-browser IDE for exploring GraphQL). We will import the “Nohac/laravel-graphiql” package to our application
$ composer require noh4ck/graphiql --dev
Run the publish command again and this time, select the package for GraphiQL
$ php artisan vendor:publish
Select the following providers from the list of possible providers by typing its corresponding number:
Provider: Graphiql\GraphiqlServiceProvider
To makes this blog extremely simple to understand, we will focus on the User model that ships with the laravel package. Let’s ensure that you have default migration file located in the database/migrations/ folder.
Now, let’s create a seeder for the users database table
$ php artisan make:seeder UsersTableSeeder
Then copy the following content to the UsersTableSeeder.php file located under the database/seeds/ folder.
// database/seeds/UsersTableSeeder.php
use Illuminate\Database\Seeder;
use App\User;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
foreach (range(1,10) as $i) {
User::create([
'name' => $faker->name,
'email' => $faker->email,
'password' => $faker->password,
]);
}
}
}
Before running the migrations and seeders, you will need to ensure that your database credentials are properly set in the .env file, then you would execute the following commands:
$ php artisan migrate
$ php artisan db:seed --class=UsersTableSeeder
After a successful execution, you should have 10 fake user accounts in your database.
Finally, Add the following function to the user model to make it easier to encrypt passwords via GraphQL mutations.
public function setPasswordAttribute($password)
{
$this->attributes['password'] = bcrypt($password);
}
In order to start using the GraphQL package, we need to define the schema. In general, schemas describe how data are structured and what data on the server can be retrieved via GraphQL. Schemas consist of GraphQL Queries and Mutations. The difference between a GraphQL query and a mutation is that a query is used to fetch data while mutation is used for INSERT/UPDATE/DELETE operations. Also, the data returned once a query or mutation is executed is structured based on the GraphQL type corresponding to the object or collection of objects being returned.
Schemas are defined inside the config/graphql.php file in order to describe how data are shaped (via GraphQL types) and what data on the server can be queried or mutated.
Let’s create a GraphQL folder under our app directory, then create 3 folders under it which are Queries, Mutations and Types.
The most basic components of a GraphQL schema are object types, which just represent a kind of object you can fetch from your service, and what fields it has. In the GraphQL schema language, we might represent the user type like this:
type User {
id: Int! //non-null Integer
name: String! //non-null String
email: String! //non-null String
}
As you can notice, the “User” type defined here is a custom type that contains 3 fields (id, name, email). Those fields could be of a scalar type or another custom type.
GraphQL comes with a set of default scalar types out of the box:
In the GraphQL schema language, the exclamation mark (!) indicates that a field is non-null.
Now, let’s represent this GraphQL type in corresponding PHP format. Below is the code snippet:
// app/GraphQL/Types/UserType.php
namespace App\GraphQL\Types;
use GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
use GraphQL\Type\Definition\Type;
use App\User;
class UserType extends GraphQLType {
protected $attributes = [
'name' => 'User', //defining the GraphQL type name
'description' => 'A User', //providing a description for the GraphQL type name
'model' => User::class, //mapping the GraphQL type to the Laravel model
];
public function fields()
{
return [
'id' => [
//defining the id field as a non-null integer
'type' => Type::nonNull(Type::int()),
'description' => 'ID of the user',
],
'name' => [
//defining the name field as a non-null string
'type' => Type::nonNull(Type::string()),
'description' => 'Name of the user',
],
'email' => [
//defining the email field as a non-null string
'type' => Type::nonNull(Type::string()),
'description' => 'Email of the user',
],
];
}
}
As specified before, queries are simply used to retrieve some info. A query essentially returns a GraphQL type, or an array of a specified type. Let’s update the config/graphql.php file as follows to define 2 queries which are “user” and “users”, as well as specifying the GraphQL type “User” that was previously created.
'schemas' => [
'default' => [
'query' => [
//retrieve a single user
'user' => App\GraphQL\Queries\UserQuery::class,
//retrieve a collection of users
'users' => App\GraphQL\Queries\UsersQuery::class,
],
'mutation' => [],
'middleware' => []
],
],
'types' => [
//user type definition
'User' => App\GraphQL\Types\UserType::class,
],
Once the queries have been defined, we can actually create them in the GraphQL folder at the following location app/graphql/queries/ while the types will be created at placed in the app/graphql/types/ folder.
A basic User Query could be as follows:
// app/GraphQL/Queries/UserQuery.php
namespace App\GraphQL\Queries;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Query;
use Rebing\GraphQL\Support\SelectFields;
use App\User;
class UserQuery extends Query {
protected $attributes = [
'name' => 'user',
];
public function authorize(array $args = [])
{
return true;
}
public function type()
{
return GraphQL::type('User'); //retrieve a single user
}
public function rules(array $args = [])
{
return [
'id' => [
'required',
'numeric',
'min:1',
'exists:users,id'
],
];
}
public function args()
{
return [
'id' => [
'name' => 'id',
'type' => Type::int(),
],
];
}
public function resolve($root, $args, SelectFields $fields)
{
return User::findOrFail($args['id']);
}
}
The users query which retrieves a collection of users would be as follows:
// app/GraphQL/Queries/UsersQuery.php
namespace App\GraphQL\Queries;
use GraphQL;
use Rebing\GraphQL\Support\Query;
use Rebing\GraphQL\Support\SelectFields;
use GraphQL\Type\Definition\Type;
use App\User;
class UsersQuery extends Query {
protected $attributes = [
'name' => 'users',
];
public function authorize(array $args = [])
{
return true;
}
public function type()
{
return Type::listOf(GraphQL::type('User')); //retrieve a collection of users
}
public function args()
{
return [
'ids' => [
'name' => 'ids',
'type' => Type::listOf(Type::int()),
],
];
}
public function rules(array $args = [])
{
return [
'ids' => [
'array',
],
'ids.*' => [
'numeric',
]
];
}
public function resolve($root, $args)
{
if (isset($args['ids'])) {
return User::find($args['ids']);
}
return User::all();
}
}
Please note that you could also use eager loading feature to improve the performance of your GraphQL queries. Also, you can use the pagination feature to limit results on queries returning large datasets.
In the examples above, we have defined 5 functions which are:
Mutations are pretty similar to queries. The only difference is that, the resolve function of a mutation contains logic to modified the targeted object.
Let’s add a CreateUserMutation, UpdateUserMutation and DeleteUserMutation to our GraphQL config file as follows:
'schemas' => [
'default' => [
'query' => [
//retrieve a single user
'user' => App\GraphQL\Queries\UserQuery::class,
//retrieve a collection of users
'users' => App\GraphQL\Queries\UsersQuery::class,
],
'mutation' => [
//create a user
'createUser' => App\GraphQL\Mutations\CreateUserMutation::class,
//update a user
'updateUser' => App\GraphQL\Mutations\UpdateUserMutation::class,
//delete a user
'deleteUser' => App\GraphQL\Mutations\DeleteUserMutation::class,
],
'middleware' => []
],
],
'types' => [
//user type definition
'User' => App\GraphQL\Types\UserType::class,
],
These mutations should be saved in the app/graphql/mutations as follows:
// app/GraphQL/Mutations/CreateUserMutation.php
namespace App\GraphQL\Mutations;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;
use Illuminate\Validation\Rule;
use App\User;
class CreateUserMutation extends Mutation
{
protected $attributes = [
'name' => 'createUser'
];
public function authorize(array $args = [])
{
return true;
}
public function rules(array $args = [])
{
return [
'name' => [
'required', 'max:50'
],
'email' => [
'required', 'email', 'unique:users,email',
],
'password' => [
'required', 'string', 'min:5'
],
];
}
public function type()
{
return GraphQL::type('User');
}
public function args()
{
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)
{
$user = new User();
$user->fill($args);
$user->save();
return $user;
}
}
An UpdateUser Mutation would look like this:
// app/GraphQL/Mutations/UpdateUserMutation.php
namespace App\GraphQL\Mutations;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;
use Illuminate\Validation\Rule;
use App\User;
class UpdateUserMutation extends Mutation
{
protected $attributes = [
'name' => 'updateUser'
];
public function authorize(array $args = [])
{
return true;
}
public function rules(array $args = [])
{
return [
'id' => [
'required', 'numeric', 'min:1', 'exists:users,id'
],
'name' => [
'required', 'max:50'
],
'email' => [
'required', 'email', 'unique:users,email,'.$args['id'],
],
'password' => [
'sometimes', 'string', 'min:5'
],
];
}
public function type()
{
return GraphQL::type('User');
}
public function args()
{
return [
'id' => [
'name' => 'id',
'type' => Type::nonNull(Type::int()),
],
'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)
{
$user = User::findOrFail($args['id']);
$user->fill($args);
$user->save();
return $user;
}
}
And finally, a DeleteUserMutation would look like this:
// app/GraphQL/Mutations/DeleteUserMutation.php
namespace App\GraphQL\Mutations;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;
use App\User;
class DeleteUserMutation extends Mutation
{
protected $attributes = [
'name' => 'deleteUser',
'description' => 'Delete a user'
];
public function authorize(array $args)
{
return true;
}
public function type()
{
return Type::boolean();
}
public function rules(array $args = [])
{
return [
'id' => [
'required', 'numeric', 'min:1', 'exists:users,id'
],
];
}
public function args()
{
return [
'id' => [
'name' => 'id',
'type' => Type::int()
]
];
}
public function resolve($root, $args)
{
$user = User::findOrFail($args['id']);
return $user->delete() ? true : false;
}
}
Note that for the DeleteUserMutation, we chose to return the scalar type “boolean” which would be true if the deletion has been successful or false otherwise.
Start the application by running the following command
$ php artisan serve
Then go to the following URL: http://127.0.0.1:8000/graphql-ui. You should see the graphiql interface. Type the following query in the left pane and click on the submit button (Play icon) to preview the results.
{
users{
id name email
}
}
You should expect something like this:
You should also be able to execute any of the mutations in a similar fashion. Let’s create a new user by executing the following
mutation createUser {
createUser(name : "John Doe", email : "[email protected]", password: "secret"){
id name email
}
}
The expected result should look like this:
Now that you are able to execute GraphQL queries and mutations, try to execute queries or mutations to see how to react based on constraints set in the validation rules. Also, feel free to try to paginated or eager load some responses. Finally, add more eloquent relationships to the User model and link them via GraphQL types as well.
So there you have it, GraphQL and Laravel. It might seem intimidating at first because it definitely comes with quite a few conceptual ideas, but if you take the time to understand the underlying concepts, I think you’ll realize that it all funnels through. Whether you decide on hopping on this GraphQL journey or not, I believe it will continue to gain in popularity as more and more companies get accustomed to it, and it might very well be on the verge to becoming one of the essential building blocks of the web in the upcoming future.
Feel free to download a more complete example we put together at the following link.
Posted in #Technologies under *Laravel, *PHP, *GraphQL