GraphQL has great potential in the way customer-facing applications request data, avoiding managing large amounts of unnecessary payloads and retrieving only what really matters. For this blog post, I will show you how to create a Laravel application.
What is GraphQL?
A Query Language. Unfortunately, it’s a simple and not objective answer. Even though GraphQL itself is something simple, for several reasons the explanation of what it is is not the simplest.
Installing and configuring dependencies
First of all, let’s configure some dependencies:
Creating a Laravel project
Let’s start a new project with the command:
composer create-project laravel/laravel graphql-laravel
If you don’t have composer installed click here to install.
Installing Sail
Laravel Sail is a command-line interface for interacting with Laravel’s standard Docker development environment. Sail provides a great starting point for building a Laravel application using PHP, MySQL and Redis without requiring any prior Docker experience.
At its core, Sail is the docker-compose.yml file, the navigation script provides a CLI with convenient methods for interacting with the Docker containers defined by the docker-compose.yml file.
Let’s install Sail as a development dependency with:
composer require laravel/sail --dev
Since we are using docker to run the development environment, we need a Dockerfile, right? To access this docker file, and after installing the sail dependency from the previous command, you need to run the sail:install
command.
php artisan sail:install
The command will ask which database you are going to use, just choose one and press enter
.
And that’s it, we can run our application with Sail using the command:
./vendor/bin/sail up -d
To make Sail easier to use and not have to run the command ./vendor/bin/sail up -d
every time we can add an alias to make the command easier, to do this just add the line below in your bash file ( ~/.bashrc
or ~/.zshrc
).
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
And then we can run Sail with:
sail up -d
Or stop it with :
sail stop
Installing Lighthouse
Lighthouse is a package for Laravel that will help us create a GraphQL API.
Let’s start by installing:
sail composer require nuwave/lighthouse
And we publish the default schema in graphql/schema.graphql
:
sail artisan vendor:publish --tag=lighthouse-schema
And also the Lighthouse configuration file:
sail artisan vendor:publish --tag=lighthouse-config
The file will be created in config/lighthouse.php
.
In order to test it in a simple way, let’s add the GraphQL Playground.
sail composer require mll-lab/laravel-graphql-playground
You can access the playground at http://localhost/graphql-playground
.
To finish the Lighthouse configuration, let’s enable CORS (Cross-Origin Resource Sharing) for your GraphQL endpoints in config/cors.php
by changing the line:
return [
- 'paths' => ['api/*', 'sanctum/csrf-cookie'],
+ 'paths' => ['api/*', 'graphql', 'sanctum/csrf-cookie'],
];
Setting up Sanctum
Sanctum is already installed in our project, to make sure check if the file config/sanctum.php
exists, then we just need to add it to Lighthouse, editing config/lighthouse.php
:
return [
- 'guard' => 'api',
+ 'guard' => 'sanctum',
];
Creating the API
Now that everything is set up let’s get started!
Updating our Schema
The first thing we will do is add API authentication to allow users to log in to their accounts. Typically, when developing a REST API, you start by defining the API endpoint. In GraphQL you have only one endpoint which will be used to execute Queries and Mutations. Every “endpoint” must be present in our GraphQL schema, so let’s start by creating our login mutation.
Add to the graphql/schema.graphql
file:
type AccessToken {
token: String!
}
input LoginInput {
email: String! @rules(apply: ["email"])
password: String!
}
extend type Mutation {
login(input: LoginInput @spread): AccessToken!
@field(solver: "App\\GraphQL\\Mutations\\AuthMutation@login")
}
Creating the first mutation
We can see that our authentication is using the AuthMutation mutation but we still need to create it with:
sail artisan lighthouse:mutation AuthMutation
You should have a new AuthMutation.php
file in app\GraphQL\Mutations
. This mutation will be responsible for receiving the email and password and handling the authentication.
namespace App\GraphQL\Mutations;
use Illuminate\Auth\AuthManager;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Nuwave\Lighthouse\Exceptions\AuthenticationException;
class AuthMutation
{
public function __construct(private AuthManager $authManager)
{
}
public function login($_, array $args)
{
$userProvider = $this->authManager->createUserProvider('users');
$user = $userProvider->retrieveByCredentials([
'email' => $args['email'],
'password' => $args['password'],
]);
if (!$user || !$userProvider->validateCredentials($user, $args)) {
throw new AuthenticationException('The provided credentials are incorrect.');
}
if ($user instanceof MustVerifyEmail && !$user->hasVerifiedEmail()) {
throw new AuthenticationException('Your email address is not verified.');
}
return [
'token' => $user->createToken('login')->plainTextToken,
];
}
}
In order to test our authentication let’s create a user using Laravel Tinker to make it easier.
sail artisan tinker
With Tinker, you can run PHP code directly from your terminal, which allows you to execute class methods. With that said, we can easily create a new user with the UserFactory that comes with the default Laravel installation:
>>> User::factory()->create(['email' => 'email@provider.com']);
The output of this command should be a new instance of User::class and as we call the create() method the data should be persisted in our database.
[!] Aliasing 'User' to 'App\Models\User' for this Tinker session.
=> App\Models\User {#3626
name: "Peggie Jaskolski PhD",
email: "email@provider.com",
email_verified_at: "2022-04-17 21:29:33",
#password: "$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi",
#remember_token: "HwJWfY62uG",
updated_at: "2022-04-17 21:29:34",
created_at: "2022-04-17 21:29:34",
id: 1,
}
Authenticating
We already have our user created, and our schema and authentication methods ready, with all that done, let’s finally try to make the request by accessing our playground http://localhost/graphql-playground
and executing our mutation:
mutation {
login(input: { email: "email@provider.com", password: "password" }) {
token
}
}
And if you followed the tutorial correctly so far, the return will be something like:
{
"date": {
"Login": {
"token": "1|4rbAzr3tqnof5Upd12q3RaGLzMbWfIsQVL8wVtAC"
}
}
}
Verifying the token
Let’s go to the last step to make sure that the generated token is really from the requested user.
For this we will add a query to the graphql/schema.graphql
file:
type Query {
me: User @auth
}
And as simple as that we can know which user is authenticated, or we receive a null if none or an invalid token is passed.
In the playground just add our token in our Authorization
Header:
{
"Authorization": "Bearer 1|4rbAzr3tqnof5Upd12q3RaGLzMbWfIsQVL8wVtAC"
}
With this we have our first GraphQL API done, so feel free to create new mutations and queries.
The complete Project can be downloaded from this repository