GraphQL is a query language for APIs and can be used to query data from a server while also allowing clients to specify exactly what data is needed.
Project Overview
Before we get started, we’ll need to get familiar with the project we are attempting to build. To do that, we will define our resources and create our GraphQL schema, which we will later use to serve our API.
Project Resources
Our application will consist of two resources: Clients and Users. These resources will be defined as object types in our GraphQL schema example
type Query {
users: [User!]!
clients: [Client!]!
user(id: ID!): User
client(id: ID!): Client
}
type User {
id: ID!
name: String!
email: String!
}
type Client {
id: ID!
name: String!
email: String!
}
Looking at the schema, we can see that we have a one-to-many relationship between our two objects.
Setting up our Laravel Project
Now that we have defined our GraphQL schema, let’s get our Laravel project up and running. Let’s start by creating a new Laravel via Composer project:
$ composer create-project –prefer-dist laravel/laravel sample-graphql
Just to make sure we have everything working
$ cd sample-graphql
$ php artisan serve
Laravel development server started: <http://127.0.0.1:8000>
Database Models and Migrations
To accomplish this task, we will utilize MySQL. Please update the database setup in the default .env file accordingly. Additionally, manually create a database in your phpMyAdmin interface.”
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db_graphql
DB_USERNAME=XXXX
DB_PASSWORD=XXXX
For this functionality, we require two tables: the `users` table and the `clients` table. The `users` table already exists in Laravel by default, so we only need to create the `clients` table. Let’s create this table using the `make:migration` command.
$ php artisan make:migration create_clients_table
Now, we need to create models for both the `users` and `clients` tables. However, the model for the `users` table already exists in the Laravel framework.
For Client:
$ php artisan make:model Client
Here’s how you can create models for Clients and their migration files, using Laravel’s Artisan command line.
Now, let’s make the necessary adjustments in the generated migration file
In the migration file, insert the code highlighted in blue below.
/database/migrations/XXX_XX_XX_XXXX_create_clients_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
//domainname/name – new fields
Schema::create(‘clients’, function (Blueprint $table) {
$table->bigIncrements(‘id’);
$table->string(‘name’);
$table->string(‘domainname’)->unique(); // Assuming domain names are unique
$table->ipAddress(‘ipaddress’); // For storing IP addresses
$table->string(‘city’);
$table->string(‘country’);
$table->string(‘state’);
$table->string(‘zipcode’);
$table->timestamps(); // This will create the `created_at` and `updated_at` columns
});
}
public function down()
{
Schema::dropIfExists(‘clients’);
}
};
Modify the existing users migration file
database/migrations/XXX_XX_XX_XXXX_create_users_table.php
For User
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create(‘users’, function (Blueprint $table) {
$table->bigIncrements(‘id’);
$table->unsignedBigInteger(‘client_id’); // Assuming users belong to a client
$table->string(‘firstname’);
$table->string(‘lastname’);
$table->string(‘password’);
$table->string(’email’)->unique(); // Assuming emails are unique
$table->string(‘status’); // e.g., active, inactive, etc.
$table->timestamps(); // This will create the `created_at` and `updated_at` columns
// Foreign key constraint (ensure the clients’ table is migrated first)
$table->foreign(‘client_id’)->references(‘id’)->on(‘clients’)->onDelete(‘cascade’);
});
}
public function down()
{
Schema::dropIfExists(‘users’);
}
};
Now that we have our migration files defined, let’s proceed to execute them against our database:
Please make sure to migrate the clients table before the users table to prevent any foreign key constraint issues.
To begin, execute the client migration file. You can use the terminal command provided in the example below. Simply copy the name of the client file and paste it into the command prompt:
php artisan migrate –path=/database/migrations/
xxxx_xx_xx_xxxxxx_create_clients_table.php
This will migrate the clients table. Once completed, you can proceed with migrating the users table in a similar manner.
$ php artisan migrate
Next, lets update models by deafening the necessary adjustments
For Client model
app/Models/Client.php
namespace App\Models;
use App\Models\User;
use Illuminate\Foundation\Auth\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Client extends Model
{
use HasFactory;
// Assuming your clients table has standard Laravel naming conventions
protected $fillable = [
‘name’, ‘domainname’, ‘ipaddress’, ‘city’, ‘country’, ‘state’, ‘zipcode’,
];
/**
* Get the users for the client.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function users()
{
return $this->hasMany(User::class);
}
}
Remember to import the User class in the Client.php file.
For User Model
app/Models/User.php
namespace App\Models;
use App\Models\Client;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Users extends Model
{
use HasFactory;
use Notifiable;
protected $fillable = [
‘client_id’, ‘firstname’, ‘lastname’, ‘username’, ‘password’, ’email’, ‘status’,
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
‘password’, ‘remember_token’,
];
/**
* User belongs to a client.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function client()
{
return $this->belongsTo(Client::class);
}
}
Remember to import the Client file in the User.php file.
Database Seeding
Now that we have our models and migrations set up, let’s seed our database. We’ll start by creating some seeder classes for our clients and users tables:
$ php artisan make:seeder ClientsTableSeeder
$ php artisan make:seeder UsersTableSeeder
Next, let’s set them up to insert some dummy data into our MySQL database:
File Path – database/seeds/UsersTableSeeder.php
namespace Database\Seeders;
use App\User;
use App\Models\User;
use App\Models\Client;
use Illuminate\Database\Seeder;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// If not, you need to seed the clients’ table first or create a client here before assigning.
User::truncate();
$faker = \Faker\Factory::create();
$password = bcrypt(‘secret’);
$status = ‘active’; // Assuming ‘active’ is a valid status for your users
$clientIds = Client::pluck(‘id’); // Efficiently fetch all client IDs
User::create([
‘client_id’ => $clientIds->random(), // Randomly select a client ID,
‘firstname’ => $faker->firstName,
‘lastname’ => $faker->lastName,
’email’ => ‘graphql@test.com’,
‘password’ => $password,
‘status’ => $status,
]);
for ($i = 0; $i < 10; ++$i) {
User::create([
‘client_id’ => $clientIds->random(), // Randomly select a client ID
‘firstname’ => $faker->firstName,
‘lastname’ => $faker->lastName,
’email’ => $faker->unique()->safeEmail, // Generate unique emails each time
‘password’ => $password,
‘status’ => $status,
]);
}
}
}
Filepath – database/seeds/ClientsTableSeeder.php
namespace Database\Seeders;
use App\Models\Client;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class ClientsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
DB::statement(‘SET FOREIGN_KEY_CHECKS=0;’);
// If using Laravel 8 or later, don’t forget to update the namespace if necessary
\App\Models\Client::truncate();
\App\Models\Client::unguard();
DB::statement(‘SET FOREIGN_KEY_CHECKS=1;’);
$faker = \Faker\Factory::create();
// Reset auto-increment value
DB::statement(‘ALTER TABLE clients AUTO_INCREMENT = 1;’);
// Creating 10 dummy client records as an example
for ($i = 0; $i < 10; $i++) {
Client::create([
‘name’ => $faker->company,
‘domainname’ => $faker->domainName,
‘ipaddress’ => $faker->ipv4,
‘city’ => $faker->city,
‘country’ => $faker->country,
‘state’ => $faker->state,
‘zipcode’ => $faker->postcode,
]);
}
}
}
/database/seeds/DatabaseSeeder.php
use Illuminate\Database\Seeder;
use Database\Seeders\UsersTableSeeder;
use Database\Seeders\ClientsTableSeeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application’s database.
*
* @return void
*/
public function run()
{
// Order matters here if your users are related to clients
// Ensure clients are seeded first so that users can be assigned to them
$this->call(ClientsTableSeeder::class);
$this->call(UsersTableSeeder::class);
}
}
Finally, let’s go ahead and run our database seeders to get some data into our database:
$ php artisan db:seed
That’s it you have successfully seeded your MySql Database!.
Laravel Lighthouse and GraphQL Server
Now that we have our database and models set up, it’s time to start building out our GraphQL server. Currently, there are several solutions available for Laravel, but for this article, we’re going to use Lighthouse. It allows developers to quickly set up a GraphQL server using Laravel with little boilerplate while also being flexible enough to allow developers to customize it to fit the needs of just about any project.
$ composer require nuwave/lighthouse:^5.0
Next, let’s publish the Lighthouse Configuration file:
$ php artisan vendor:publish –tag=lighthouse-config
So let’s go ahead and create our schema file and set up our user object type and query:
$ mkdir graphql
From your preferred development directory, create a directory for a new project and cd into it:
$ cd graphql/
Node Setup – Difference between npm/nvm
npm serves as a package manager designed to handle JavaScript packages and dependencies. On the other hand, nvm functions as a version manager specifically tailored for the installation and management of multiple Node.js versions on a single machine. NVM Installation process
This allows you to switch between different Node.js versions easily. This should be more than 14
$ nvm install 16
$ nvm use 16
Initialize a new Node.js project with npm (or another package manager you prefer, such as Yarn):
$ npm init –yes
Your project directory now contains a package.json file.
Step 2: Install Dependencies
- apollo-server is the core library for Apollo Server itself, which helps you define the shape of your data and how to fetch it.
- GraphQL is the library used to build a GraphQL schema and execute queries against it.
Run the following command to install both of these dependencies and save them in your project’s node_modules directory:
$ npm install apollo-server graphql
You’ll need to install apollo-server, graphql, and mysql2. If you haven’t installed them yet, you can do so by running. This package is used to connect you MySql database.
$ npm install apollo-server graphql mysql2
Also create an empty index.js file in your project’s directory:
$ touch index.js
To keep things simple, index.js will contain all of the code for this example application.
Step 3: Define your GraphQL schema
Every GraphQL server (including Apollo Server) uses a schema to define the structure of data that clients can query. In this example, we’ll create a server for querying a collection of users and clients.
Open index.js in your preferred editor and paste the following into it:
const { ApolloServer, gql } = require(“apollo-server”);
const mysql = require(“mysql2/promise”);
const dbConfig = {
host: “localhost”,
user: “XXXX”, // database username*
database: “db_graphql”, // database name*
password: “XXXX”, // database password*
};
const initDbConnection = async () => {
return await mysql.createConnection(dbConfig);
};
// Defining our schema
const typeDefs = gql`
# A “Client” type that defines the queryable fields for every client in our data source.
# A “User” type that defines the queryable fields for every user in our data source.
# The “Query” type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the “users” query returns an array of zero or more Users, and
# If we need to get some data from db we can use like this
type User {
id: ID!
firstname: String
lastname: String
email: String
status: String
client_id: ID
password: String
}
# below the query is for fetching data from the database
type Query {
getUsers: [User]
}
`;
// Step 4: Define a Resolver
// Resolvers define the technique for fetching the types defined in the
// schema. This resolver retrieves users from the “users” array above.
const resolvers = {
Query: {
getUsers: async () => {
try {
const connection = await initDbConnection();
const [rows] = await connection.execute(“SELECT * FROM users”);
return rows.map((row) => ({
id: row.id,
firstname: row.firstname,
lastname: row.lastname,
email: row.email,
status: row.status,
client_id: row.client_id,
// Ensure all fields required by your GraphQL schema are mapped here
}));
} catch (error) {
console.error(“Error fetching users:”, error);
// Handle errors appropriately; you might want to throw an error or return an empty array
throw new Error(“Failed to fetch users”);
}
// console.log(“Users data: “, rows);
},
},
};
// Step 5: Create an instance of Apollo Server
// Creating the Apollo Server with our type definitions, resolvers, and telling it
// to use the playground (useful for testing and exploring your API)
const server = new ApolloServer({ typeDefs, resolvers });
// Starting the server
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Step 6: Start your Server
$ node index.js
You should see the following output:
🚀 Server ready at http://localhost:4000/
Please refer to the code in the Git repository below for your reference
git clone https://github.com/Deepakdckap/Laravel_Graphql_index.js.git
When starting up Playground, click on the “URL Endpoint” tab, and type in http://localhost:4000/graphql to point GraphQL Playground to our server. On the left side of the editor, we can query for our data, so let’s start by asking for all the users that we seeded the database with:
query Query {
getUsers {
id
firstname
lastname
}
}
When you hit the play button in the middle of the IDE (or click Ctrl+Enter), you’ll see the JSON output of our server on the right side, which will look example like this:
“data”: {
“getUsers”: [
{
“id”: “1”,
“firstname”: “Hildegard”,
“lastname”: “Pagac”,
“email”: “graphql@test.com”
},
{
“id”: “2”,
“firstname”: “Taya”,
“lastname”: “Stracke”,
“email”: “premnath@gmail.com”
},
]
}
Note: Because we used Faker to seed our database, the data so the fields will be different.
GraphQL Mutation:
Now that we can query our data, let’s create some mutations to create some new users and with the existing client_id.
In your existing typeDefs function do the following mutation definition:
input userInput {
id: ID!
firstname: String
lastname: String
email: String
password: String
status: String
client_id: ID
}
#Mutation
type Mutation {
loginUser(userInput: userInput): User
}
Then, ensure you have `bcrypt` installed in your project. If not, you can install it using npm or yarn:
$ npm install bcrypt
const bcrypt = require(‘bcrypt’);
const saltRounds = 10; // The cost factor controls how much time is needed to calculate a single bcrypt hash. The higher the cost factor, the more hashing rounds are done.
In your index.js existing resolvers function the following mutation (after the Query function)
Mutation: {
loginUser: async (_, { userInput }) => {
const connection = await initDbConnection();
// Hash the password before storing it in the database
const hashedPassword = await bcrypt.hash(userInput.password, saltRounds);
const query = `INSERT INTO users (id, firstname, lastname, password, email, client_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`;
// Use the hashedPassword instead of the plain password
const values = [
userInput.id,
userInput.firstname,
userInput.lastname,
hashedPassword, // Use the hashed password here
userInput.email,
userInput.client_id,
userInput.status,
];
// Execute the query with the values
await connection.execute(query, values);
// console.log(“User data inserted successfully.”);
// Don’t forget to close the connection when done
connection.end();
},
},
In the GraphQL Playground, click on the “Root” tab on the left side, then select “mutation.” Follow the next steps as directed.
Root -> Mutation -> loginUser -> Arguments -> userInput
Now that we have our createUser mutation field set up, let’s go ahead and run it in GraphQL Playground with the following: below example
mutation Mutation($userInput: userInput) {
loginUser(userInput: $userInput) {
client_id,
id,
firstname,
lastname,
email,
status,
password
}
}
Assign the values for the required variables, here is an example below
{
“userInput”: {
“id”: 12, #auto increment id
“firstname”: “john”,
“lastname”: “bekam”,
“email”: “johnbekam@gmail.com“,
“password”: “johnbekam@143”, #password will be encrypted while inserting
“client_id”: 7, #existing client_id in the database
“status”: “active”
}
}
Record will be inserted successfully. Thank you for taking the time to read my blog!