GraphQL In Laravel — PART 2

We are going to create GraphQL project in Laravel to understand how GraphQL works. You can refer to my previous article on GraphQL to understand the features and advantages of it.

Let’s create a Laravel project.

composer create-project laravel/laravel graphql-project

For this project, we are going to make use of JWT tokens. So, let’s install the JWT and GraphQL packages in our project.

composer require tymon/jwt-auth
composer require folklore/graphql

Add the service providers to the providers array in the config/app.php config file as follows:

'providers' => [   ...Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
Folklore\GraphQL\ServiceProvider::class,
]

Publish the configuration files:

$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"$ php artisan vendor:publish --provider="Folklore\GraphQL\ServiceProvider"

Run the php artisan jwt:secret to generate the secret key and add this key value to the JWT_SECRET in the .env file.

Create Models and Migration

In addition to the User model which comes by default in the Laravel application, we will create the Post model and its corresponding migration files.

php artisan make:model Post -m

Let’s open the migration of the Post model and update the up method as below:

// database/migrations/TIMESTAMP_create_posts_table.phppublic function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->string('title', 100);
$table->string('body');
$table->timestamps();
});
}

Run the migrations:

php artisan migrate

Define relationships between models

We’ll start by defining the relationship between a user and a post, which will be a one-to-many relationship. That is, a user can add as many posts as they wish, but a post can only belong to one user. Add the code below inside the User model.

// app/User.php
public function
posts()
{
return $this->hasMany(Post::class);
}

Next, let’s define the inverse of the relationship on the Post model

// app/Post.phppublic function user()
{
return $this->belongsTo(User::class);
}

Also, to use JWT in our project, add below two methods in User.php model.

public function getJWTIdentifier()
{
return $this->getKey();
}

public function getJWTCustomClaims()
{
return [];
}

Overall, the User.php file would look like below:

<?php

namespace
App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject {
use Notifiable;

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

/**
* The attributes that should be hidden for arrays.
*
*
@var array
*/
protected $hidden = [
'password', 'remember_token',
];

public function posts()
{
return $this->hasMany(Post::class);
}

public function getJWTIdentifier()
{
return $this->getKey();
}

public function getJWTCustomClaims()
{
return [];
}
}

In the config/auth.php file, add jwt as the driver in api array and also change default guard to api.

'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],

'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],

Creating GraphQL Types

We will create GraphQL types corresponding to our models. Create a GraphQL folder within the app folder. Within GraphQL folder, create Types folder. Within app/GraphQL/Types folder, create a new UserType.php file and paste the following code in it:

// app/GraphQL/Types/UserType.php
<?php

namespace
App\GraphQL\Types;

use Folklore\GraphQL\Support\Type as GraphQLType;
use GraphQL;
use GraphQL\Type\Definition\Type;

class UserType extends GraphQLType {

protected $attributes = [
'name' => 'User',
'description' => 'A user',
];

public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The id of a user',
],
'name' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The description of a user',
],
'email' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The email address of a user',
],
'posts' => [
'type' => Type::listOf(GraphQL::type('Post')),
'description' => 'The user posts',
],
'created_at' => [
'type' => Type::string(),
'description' => 'Date a user was created',
],
'updated_at' => [
'type' => Type::string(),
'description' => 'Date a user was updated',
],
];
}

protected function resolveCreatedAtField($root, $args)
{
return (string)$root->created_at;
}

protected function resolveUpdatedAtField($root, $args)
{
return (string)$root->updated_at;
}

}

The UserType contains the exact same fields corresponding to those in the users tables. It also contains an additional posts field which is of PostType(we will create this next). This will allow us to retrieve posts published by users. Lastly, because Laravel will automatically cast the created_at and the updated_at fields to an instance of carbon, we need to specifically define how we want these fields to be resolved. So we define a resolve function for each of the fields by using the convention resolve[FIELD_NAME]Field. As you can see, we are simply casting the fields to string.

Create PostType in app/GraphQL/Types folder, and paste this following code.

// app/GraphQL/Types/PostType.php
<?php

namespace
App\GraphQL\Types;

use Folklore\GraphQL\Support\Type as GraphQLType;
use GraphQL;
use GraphQL\Type\Definition\Type;

class PostType extends GraphQLType {

public function attributes()
{
return [
'name' => 'Post',
'description' => 'Post By User',
];
}

public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The id of the post',
],
'title' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The title of the post',
],
'body' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The body of the post',
],
'user' => [
'type' => Type::nonNull(GraphQL::type('User')),
'description' => 'The post by the user',
],
'created_at' => [
'type' => Type::string(),
'description' => 'Date a post was created',
],
'updated_at' => [
'type' => Type::string(),
'description' => 'Date a post was updated',
],
];
}

protected function resolveCreatedAtField($root, $args)
{
return (string)$root->created_at;
}

protected function resolveUpdatedAtField($root, $args)
{
return (string)$root->updated_at;
}
}

Same as we did with the UserType. The userfield is of the UserType, which will be used to retrieve the user (using the relationship defined above) that published the post.

Now, let’s make GraphQL aware of our types by adding them to config/graphql.php:

// config/graphql.php'types' => [
'User' => \App\GraphQL\Types\UserType::class,
'Post' => \App\GraphQL\Types\PostType::class,
],

Mutations

Create Mutations folder within the app/GraphQL folder. Let’s create a SignUpMutation and LoginMutation for user sign up and login respectively.

Within Mutations, create a new SignUpMutation.php file and paste the following code into it:

// app/GraphQL/Mutations/SignUpMutation.php
<?php
namespace App\GraphQL\Mutations;use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Mutation;
use App\User;
class SignUpMutation extends Mutation {public function attributes()
{
return [
'name' => 'signUp',
];
}public function type()
{
return Type::string();
}
public function args()
{
return [
'name' => [
'name' => 'name',
'type' => Type::nonNull(Type::string()),
'rules' => ['required'],
],
'email' => [
'name' => 'email',
'type' => Type::nonNull(Type::string()),
'rules' => ['required', 'email', 'unique:users'],
],
'password' => [
'name' => 'password',
'type' => Type::nonNull(Type::string()),
'rules' => ['required'],
],
];
}
public function resolve($root, $args)
{
$user = User::create([
'name' => $args['name'],
'email' => $args['email'],
'password' => bcrypt($args['password']),
]);
// generate token for user and return the token
return auth()->login($user);
}
}

We define the type this mutation will return, then define the args method which returns the list of argument this mutation will accept. We also define some validation rule for each argument. The resolve method handles the creating of the user. Lastly, we log the new user in by generating a token (JWT), then we return the token.

Next, let’s add the mutation to config/graphql.php:

// config/graphql.php
'schemas'
=> [
'default' => [
///....
'mutation' => [
'signUp' => App\GraphQL\Mutations\SignUpMutation::class,
],
],
],

Create LogInMutation.php file within the app/GraphQL/Mutations folder for user log in and paste the below code:

// app/GraphQL/Mutations/LogInMutation.php<?php

namespace
App\GraphQL\Mutations;

use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Mutation;

class LogInMutation extends Mutation{

public function attributes()
{
return [
'name' => 'logIn',
];

}

public function type()
{
return Type::string();
}

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

public function resolve($root, $args)
{
$credentials = [
'email' => $args['email'],
'password' => $args['password'],
];

$token = auth()->attempt($credentials);

if (!$token) {
throw new \Exception('Unauthorized!');
}

return $token;
}
}

This is similar to previous to a similar mutation. Here, we accept the user email and password for log in an return the JWT token in response.

Next, add the mutation to config/graphql.php:

// config/graphql.php'schemas' => [
'default' => [
///...
'mutation' => [
///...
'logIn' => App\GraphQL\Mutations\LogInMutation::class,
],
],
],

Once a user is authenticated, the user will be able to publish a new post. Let’s create the mutation for publishing a new post. Within app/GraphQL/Mutations, create a new NewPostMutation.php file and paste the following code into it:

// app/GraphQL/Mutations/NewPostMutation.php<?php

namespace
App\GraphQL\Mutations;

use App\Post;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Mutation;

class NewPostMutation extends Mutation {

public function attributes()
{
return [
'name' => 'newPost',
];

}

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

public function args()
{
return [
'title' => [
'name' => 'title',
'type' => Type::nonNull(Type::string()),
'rules' => ['required', 'max:50'],
],
'body' => [
'name' => 'body',
'type' => Type::nonNull(Type::string()),
'rules' => ['required', 'max:255'],
],
];
}

public function authenticated($root, $args, $currentUser)
{
return !!$currentUser;
}

public function resolve($root, $args)
{
$post = new Post();

$post->user_id = auth()->user()->id;
$post->title = $args['title'];
$post->body = $args['body'];

$post->save();

return $post;
}
}

Here we return the PostType in the response. Only the authenticated user can create the post. We define an authenticated method whose third parameter will be the currently authenticated user or null if a user is not logged in. So we simply cast whatever currentUser holds to boolean. That is, the authenticated method will return true if the user is authenticated and hence proceed with the rest of the mutation. If the method returns false, the mutation will throw an “Unauthenticated” error message. In the resolve method, we accept the title, body and user id of the currently authenticated user and create a post. Finally, we return the newly created post.

Next, add the mutation to config/graphql.php:

// config/graphql.php'schemas' => [
'default' => [
///...
'mutation' => [
///...
'newPost' => App\GraphQL\Mutations\NewPostMutation::class,
],
],
],

Query

Let’ create our first query. This query is used to fetch all the posts. Create aQuery folder within the app/GraphQL folder, and within Query folder, create AllPostsQuery.php file and paste the below code:

// app/GraphQL/Query/AllPostsQuery.php<?php

namespace
App\GraphQL\Query;

use App\Post;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Query;
use GraphQL\Type\Definition\ResolveInfo;

class AllPostsQuery extends Query{

protected $attributes = [
'name' => 'allPosts',
];

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

public function resolve($root, $args, $context, ResolveInfo $info)
{
$fields = $info->getFieldSelection();

$posts = Post::query();

foreach ($fields as $field => $keys) {
if ($field === 'user') {
$posts->with('user');
}
}

return $posts->latest()->get();
}
}

We define the query type to be a list of PostType. Then we define the resolve method, which will handle the actual fetching of posts. The getFieldSelection method to get the names of all fields selected when the query is run. Using these fields, we eager-load the respective related models then return the posts with the latest ones.

Let’s add the query to config/graphql.php:

// config/graphql.php'schemas'               => [
'default' => [
'query' => [
'allPosts' => App\GraphQL\Query\AllPostsQuery::class,
],
///...
],
],

Testing the API

You can test the APIs directly from the browser URL or Postman App. Also, there is Insomnia which provides GraphQL out of the box. Let’s use Insomnia to test our API.

First, let’s register the user:

mutation{
signUp(name: "sharath",email: "sharathkumar@test.com", password : "password")
}

The response will be as shown below:

{
"data": {
"signUp": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvZ3JhcGhxbHByb2plY3QudGVzdFwvZ3JhcGhxbCIsImlhdCI6MTU1MDU1NTgzOSwiZXhwIjoxNTUwNTU5NDM5LCJuYmYiOjE1NTA1NTU4MzksImp0aSI6InoxOVJMdTNhRmVqUTRUVjQiLCJzdWIiOjQsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.cdp9qF9vGGM6KmzX--jHp_R9YB5F1ToDDkWH8cZEQWQ"
}
}

Once the user is registered, let’s use LogIn mutation to get the token.

mutation{
logIn(email : "sharathkumar@test.com",password : "password")
}

The following is the response where we get the token:

{
"data": {
"logIn": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvZ3JhcGhxbHByb2plY3QudGVzdFwvZ3JhcGhxbCIsImlhdCI6MTU1MDU1NTUwNywiZXhwIjoxNTUwNTU5MTA3LCJuYmYiOjE1NTA1NTU1MDcsImp0aSI6IktLR1pKV096emhUU214QUMiLCJzdWIiOjIsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.nDY6WjAA3d_rzNUV-l0SFwqCm9ppx1Nbf6wLvXhAzTU"
}
}

Once we have the token, we can pass it in the header and get the posts published by the user.

Query request and response for the allPosts query.

// fetch all posts//request{
allPosts{
title
body
user{
name
}
}
}
//response{
"data": {
"allPosts": [
{
"title": "Hello",
"body": "dasdadddddddddddddddddds dsads ds",
"user": {
"name": "sharath"
}
}
]
}
}

Conclusion

In this article, we have seen how to build an API using Laravel and GraphQL. We covered things like authentication, querying nested resources and eager loading related models. Feel free to play with this API by adding your own features, this will help solidify your understanding of working with Laravel and GraphQL.

Thanks!!Happy Coding!!

I code for living.