Application Programming Interfaces (APIs) are a crucial element of modern web development. They allow different applications and services to communicate with each other, sharing data and functionality, which enables developers to create complex, interconnected systems that would be impossible with standalone applications.
One of the most popular and powerful API technologies today is GraphQL. Unlike traditional REST APIs, which have fixed endpoints and return predefined data structures, GraphQL APIs allow clients to precisely define the data they need and receive it in a predictable and hierarchical format. This allows for more flexible and efficient data fetching and eliminates the need for over-fetching or under-fetching data.
In this tutorial, we will learn how to create a GraphQL API with Laravel, a popular PHP web framework. We will be creating a simple student model, seeding the database with dummy data, setting up a database connection, and creating a GraphQL server by defining our API's schema, queries, and mutations. We’ll also learn how to make requests to our API (test our endpoints) using a tool like Insomnia or Postman. By the end of this tutorial, you will have a working GraphQL API that you can continue to expand and improve.
Here's a link to the GitHub repo for this article.
There are several advantages to using GraphQL for your API:
- Flexibility: As mentioned above, with GraphQL, clients can specify the data they need and receive it in a predictable and hierarchical format. This allows for more flexible and efficient data fetching and eliminates the tendency of over-fetching or under-fetching data.
- Better performance: Because GraphQL APIs allow clients to specify precisely the data they need, clients can often fetch it in a single request, reducing the number of roundtrips to the server and improving performance.
- Strongly typed schema: GraphQL schemas are strongly typed, which means that the data returned by a GraphQL API are always in the same format. This makes it easier to predict the shape of the data and reduces the need for clients to handle edge cases and unexpected data structures.
- Ease of use: GraphQL is a simple and intuitive query language that’s easy for developers to learn and use. It also has a large and active developer community, with many libraries and tools (e.g., Apollo, Relay, Prisma, and GraphiQL) available to help with common tasks.
- Backward compatibility: Because GraphQL allows clients to specify the data they need, it is possible to make backwards-compatible changes to the API without breaking existing clients.
Prerequisites
To create a GraphQL API with Laravel, you will need to have a basic understanding of the Laravel framework and the GraphQL query language.
Additionally, make sure to have the following installed on your machine:
- PHP 7.4 or higher
- Composer
- MySQL
- Laravel 7.0 or higher
Step 1: Setting up the Laravel project
Open your terminal and navigate to the directory where you want to create your project. Then, run the following command to create a new Laravel project:
composer create-project --prefer-dist laravel/laravel graphql-tutorial
This will create a new Laravel project in a directory called graphql-tutorial
.
Step 2: Setting up the database
Next, we need to set up a database connection. Open the .env
file in the root of your project and update the following lines with your database credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=YOUR_DATABASE_NAME
DB_USERNAME=YOUR_DATABASE_USERNAME
DB_PASSWORD=YOUR_DATABASE_PASSWORD
Step 3: Creating the student model
Next, we will create a student model and a corresponding migration file to create a students
table in the database. Run the following command to create the model and the migration file:
php artisan make:model Student -m
This will create a Student
model and a migration file in the database/migrations
directory. Open the migration file and add the following columns to the students
table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateStudentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->integer('age');
$table->string('country');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('students');
}
}
Next, run the following command to create the students
table in the database:
php artisan migrate
You’ll also want to add these fields to the Student model in app/Student.php
:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
protected $fillable = ["name", "email", "age", "country"];
}
Step 4: Creating a seeder file
Next, we will create a seeder file to seed the students
table with dummy data:
php artisan make:seeder StudentTableSeeder
This will create a seeder file called StudentTableSeeder.php
in the database/seeds
directory.
Open the seeder file and add the following code:
<?php
use Illuminate\Database\Seeder;
use App\Student;
class StudentTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Create 10 students
factory(Student::class, 10)->create();
}
}
This code uses the Laravel factory to create 10 students and insert them into the students
table. Next, open the DatabaseSeeder.php
file and add the following line to the run()
method:
$this->call(StudentTableSeeder::class);
This will run the StudentTableSeeder
when you run the db:seed
command. Finally, run the following command to seed the students
table with dummy data:
php artisan db:seed
Step 5: Installing the rebing/graphql-laravel package
Next, we will install the rebing/graphql-laravel
package, which will allow us to easily create a GraphQL server in Laravel.
composer require rebing/graphql-laravel
This will add the necessary dependencies to your Laravel project. Once the package is installed, run the following command to publish the GraphQL configuration file:
php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider"
Step 6: Creating the GraphQL server
Next, we will create the GraphQL server. First, create a graphql
directory in the routes
directory. Then, create a file called StudentType.php
in the routes/graphql
directory and add the following code:
<?php
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
class StudentType extends GraphQLType
{
protected $attributes = [
'name' => 'Student',
'description' => 'A student',
'model' => \App\Student::class
];
public function fields(): array
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The id of the student'
],
'name' => [
'type' => Type::string(),
'description' => 'The name of the student'
],
'email' => [
'type' => Type::string(),
'description' => 'The email of the student'
],
'age' => [
'type' => Type::int(),
'description' => 'The age of the student'
],
'country' => [
'type' => Type::string(),
'description' => 'The country of the student'
],
'created_at' => [
'type' => Type::string(),
'description' => 'The date the student was created',
'resolve' => function($model) {
return $model->created_at;
}
],
'updated_at' => [
'type' => Type::string(),
'description' => 'The date the student was last updated',
'resolve' => function($model) {
return $model->updated_at;
}
]
];
}
}
The code above creates a GraphQL type called StudentType
that represents a student in the database. It defines fields for the id
, name
, email
, age
, country
, created_at
, and updated_at
columns of the students
table.
In the fields()
function, you’ll notice each field in the StudentType
has type
and description
parameters. Fields in a GraphQL type require a type
and description
because these parameters provide important information for the GraphQL server to understand and validate the data being requested or provided by the client. The type
specifies the data type of the field, such as a string or integer, and the description
provides additional information about the field, such as its purpose or expected format. This information is used by the GraphQL server to properly handle and respond to client queries and mutations, as well as by various tools, such as GraphQL clients and IDEs, to generate better documentation and support for developers.
Now, create a file called StudentsQuery.php
in the routes/graphql
directory. Open the file and add the following code:
<?php
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;
class StudentsQuery extends Query
{
protected $attributes = [
'name' => 'students'
];
public function type(): Type
{
return Type::listOf(GraphQL::type('student'));
}
public function args(): array
{
return [
'id' => [
'name' => 'id',
'type' => Type::int()
],
'name' => [
'name' => 'name',
'type' => Type::string()
],
'email' => [
'name' => 'email',
'type' => Type::string()
],
'age' => [
'name' => 'age',
'type' => Type::int()
],
'country' => [
'name' => 'country',
'type' => Type::string()
]
];
}
public function resolve($root, $args)
{
if (isset($args['id'])) {
return Student::where('id', $args['id'])->get();
}
if (isset($args['name'])) {
return Student::where('name', $args['name'])->get();
}
if (isset($args['email'])) {
return Student::where('email', $args['email'])->get();
}
if (isset($args['age'])) {
return Student::where('age', $args['age'])->get();
}
if (isset($args['country'])) {
return Student::where('country', $args['country'])->get();
}
return Student::all();
}
}
The code we have above defines a GraphQL query field for a list of students. The protected $attributes
array sets the field name as "students". Thus, when a client application makes a query, it will use this name to request the list of students. The public function type()
returns the type of field. In this case, it is using the helper function Type::listOf()
to indicate that the field is a list of items. The argument passed to the function, GraphQL::type('student')
, specifies that each item in the list is of the Student
type. Therefore, when the client makes a query for this field, the server will know that it is expecting a list of student objects and will retrieve and return the appropriate data to the client.
The public function args()
returns an array of input fields for the query. We’ll use these fields to retrieve data from our GraphQL server. The name
parameter for each field specifies how data from this field will be retrieved on the client side, and the type
parameter specifies the type of data that will be retrieved from this field.
The resolve
function in a GraphQL query is responsible for fetching data from the server based on the input arguments. The function takes in two parameters, $root
and $args
. $root
refers to the root object of the query, and $args
refers to the input arguments passed to the query.
In the function, we start by checking whether the id
argument is passed in the query. If it is, we search for the id in our database using Eloquent's where
method, get the result using the get
method, and return it to our client. When the name
argument is passed, we return all the students with the given name by using Eloquent's where
method and getting the result by using the get
method. The same logic applies to the email
, age
, and country
arguments. If none of these arguments are passed, it returns all the students by using the all
method.
Mutation
You should know this already, but a GraphQL mutation is a type of operation that allows a client to modify data on the server. It is similar to a POST, PUT, or PATCH request in REST. Here are the mutations that our server needs:
- Create: Allows the client to create a new student on the server.
- Update: Allows the client to update an existing student on the server.
- Delete: Allows the client to delete a student on the server.
For our first mutation, create a file called CreateStudentMutation.php in the routes
directory and add the following code:
<?php
use App\Student;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;
class CreateStudentMutation extends Mutation
{
protected $attributes = [
'name' => 'createStudent'
];
public function type(): Type
{
return GraphQL::type('student');
}
public function args(): array
{
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())
],
'age' => [
'name' => 'age',
'type' => Type::nonNull(Type::int())
],
'country' => [
'name' => 'country',
'type' => Type::nonNull(Type::string())
]
];
}
public function resolve($root, $args)
{
$student = new Student();
$student->name = $args['name'];
$student->email = $args['email'];
$student->age = $args['age'];
$student->country = $args['country'];
$student->save();
return $student;
}
}
Above, we created a GraphQL mutation called createStudent
that allows clients to create a new student in our database. It takes the name
, email
, age
, and country
of the student as arguments.
The protected $attributes
array specifies how a client utilizes the mutation. The public function type()
returns the type of mutation, the student
object.
The args()
function returns an array of arguments. These fields will be created for each mutation entry. Each entry has a name
(the name of the database column) and a (data) type
like int
and String
. The Type::nonNull
constraint ensures that null values are not entered into the database.
The resolve
function does the final work of entering the values provided into the database. In this function, we create an instance of the Student
model, save the values for name
, email
, age
, and country
in some variables, and save the values to our database with $student->save()
.
Next, to update student entries in our database, create a file called UpdateStudentMutation.php in the routes
directory and add the following code:
<?php
namespace App\graphql\Mutations;
use App\Student;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;
class UpdateStudentMutation extends Mutation
{
protected $attributes = [
"name" => "updateStudent",
];
public function type(): Type
{
return GraphQL::type("Student");
}
public function args(): array
{
return [
'name' => [
'name' => 'name',
'type' => Type::nonNull(Type::string())
],
'email' => [
'name' => 'email',
'type' => Type::nonNull(Type::string())
],
'age' => [
'name' => 'age',
'type' => Type::nonNull(Type::int())
],
'country' => [
'name' => 'country',
'type' => Type::nonNull(Type::string())
]
];
}
public function resolve($root, $args)
{
$student = Student::findOrFail($args["id"]);
$student->fill($args);
$student->save();
return $student;
}
}
The code for CreateStudentMutation is very similar to the code for UpdateStudentMutation. The only difference is in the resolve
function, where we search for the specific student to update. With Student::findOrFail($args["id"])
, we take the student id
supplied by a client application and search for it in the database. With the fill
function, we replace all of the data in the row of the id supplied with the new data provided. Then, we save the new data in the database.
To delete a student from the database, create a file called DeleteStudentMutation.php in the routes
directory and add the following code:
<?php
namespace App\graphql\Mutations;
use App\Student;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;
class DeleteStudentMutation extends Mutation
{
protected $attributes = [
"name" => "deleteStudent",
"description" => "Delete a student",
];
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)
{
$student = Student::findOrFail($args["id"]);
return $student->delete() ? true : false;
}
}
To delete a student from our database, all we need is an id, which we get from a client application. "rules" => ["required"]
ensures that an id is provided. With findOrFail
, we search for the id in our database, and the delete()
function deletes all of the data with that id.
Registering our query, mutations, and type
The last step is to register the type, query, and mutations that we just created in the GraphQL configuration file, config/graphql.php
.
return [
...
'schemas' => [
'default' => [
'query' => [
'students' => App\GraphQL\Queries\StudentsQuery::class,
],
'mutation' => [
// Create a student
'createStudent' => App\GraphQL\Mutations\CreateStudentMutation::class,
// update student
'updateStudent' => App\GraphQL\Mutations\UpdateStudentMutation::class,
// delete a student
'deleteStudent' => App\GraphQL\Mutations\DeleteStudentMutation::class,
],
'middleware' => [],
'method' => ['get', 'post'],
],
],
'types' => [
'Student' => App\GraphQL\Types\StudentType::class,
],
...
]
Step 7: Testing the endpoints
Now that the GraphQL server is set up, we can test the endpoints. Open your browser and navigate to http://localhost:8000/graphql
. You should see the GraphQL Playground interface. In the left-hand panel, type the following query to fetch all students:
{
students {
id
name
email
age
country
}
}
Query Students
Then, click the "Play" button to execute the query. You should see a list of students in the right-hand panel.
To test creating a student with a mutation, you can use the following mutation:
mutation {
createStudent(name: "New Student", email: "newstudent@example.com", age: 20, country: "Canada") {
id
name
email
age
country
}
}
Create a Student
To test updating a student with a mutation, you can use the following mutation:
mutation {
updateStudent(id: 1, name: "Updated Student", email: "updatedstudent@example.com", age: 21, country: "USA") {
id
name
email
age
country
}
}
Update a Student
You can execute these mutations in the same way as the queries, by typing the mutation in the left-hand panel and clicking the "Play" button.
To delete a student from our database, we use the following mutation:
mutation {
deleteStudent(id : 1)
}
Delete a Student
Conclusion
In this article, we’ve learned how to create a GraphQL API using Laravel and the rebing/graphql-laravel package. We’ve also created a Student model, seeded the database with dummy data, set up a database connection, created the GraphQL server, and tested the endpoints. Now, you can create your own GraphQL APIs with Laravel and the graphql-laravel package.
I hope you found this tutorial helpful. Thank you for following along!