GraphQL and Search Engine with Laravel 8

Thomas Sanctorum
Nerd For Tech
Published in
4 min readApr 20, 2021

--

I use GraphQL as a replacement for the REST APIs, allowing me to have a single entry point and fetch just what I need when I need it with the possibility to browse my Eloquent relationships. This also allows me to drastically reduce the number of Controllers for a large number of Models.

The starting point will depend on your actual environnement, personally, I use Laradock, a Docker Compose stack that provides many usefull services ready to use. The following are specific to my way of working and are for information only.

docker-compose up -d workspace nginx mysql phpmyadmindocker-compose exec workspace bashcomposer create-project laravel/laravel .php artisan migrate // after editing properly my .env to use same mysql credential as Laradock

For making our GraphQL API, We are going to use the nuwave/lighthouse package wich embark necessary functionality and schema implementation.

I also want to add an search engine. it’s optional but i find out that it will be really usefull to find our resources by keywords, we will use Laravel Scout with the tntsearch drivers for storing our index into an sqlite database inside our app, it’s a simple drivers wich can make some trouble, mostly with email indexing, you can use others drivers like Algolia, Elastic, TypeSens, MeiliSearch ... If you don’t want it, just ignore step around Scout and tntsearch. I really prefered using index system than make a ugly chain of whereLike() somewhere in my code !

composer require nuwave/lighthouse laravel/scout teamtnt/laravel-scout-tntsearch-driver

Once our packages are installed, we will configure our application to use them, first we want to add Laravel Scout and tntseach into our service Providers by editing the config/app.php file :

'providers' => [  // default Providers ...  Laravel\Scout\ScoutServiceProvider::class,
TeamTNT\Scout\TNTSearchScoutServiceProvider::class,
]

Then we will publish some files :

php artisan vendor:publish --provider=”Laravel\Scout\ScoutServiceProvider”php artisan vendor:publish --tag=lighthouse-schema

We will edit the newly published config/scout.php to add the default configuration of tntsearch :

'tntsearch' => [
'storage' => storage_path(),
'fuzziness' => env('TNTSEARCH_FUZZINESS', false),
'fuzzy' => [
'prefix_length' => 2,
'max_expansions' => 50,
'distance' => 2
],
'asYouType' => false,
'searchBoolean' => env('TNTSEARCH_BOOLEAN', false),
'maxDocs' => env('TNTSEARCH_MAX_DOCS', 500),
],

And precise in our .env that we want to use it :

SCOUT_DRIVER=tntsearch

For the rest, I want to be lazy and work with my User model, I have to edit it like this for tell Scout to index some fields :

use Laravel\Scout\Searchable;class User extends Authenticatable
{
use HasFactory, Notifiable, Searchable;
/**
* Get the indexable data array for the model.
*
* @return array
*/
public function toSearchableArray() : array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email
];
}
// rest of User Model

Laravel already provides a Factory for this Model (database/factories/UserFactory), so I can directly use Laravel Tinker to generate a hundred of fake users :

php artisan tinker>> User::factory()->count(100)->create() // This will store 100 User in my databasephp artisan tntsearch:import App\\Models\\User

So, now we have GraphQL implemented and some data to work with. We can start to try this ! For it, I using Altaïr https://addons.mozilla.org/fr/firefox/addon/altair-graphql-client/, an extension for Mozilla Firefox, feel free to use the client you like. Note that an playground for Laravel exist https://github.com/graphql/graphql-playground but I don’t like having to install it on every project where I implement GraphQL and prefer to use an unique external tool.

POST http://localhost/graphql{
users {
data {
name
email
}
paginatorInfo {
currentPage
hasMorePages
lastPage
}
}
}

Wich will return something like this :

{
"data": {
"users": {
"data": [
{
"name": "Dr. Alan Mitchell",
"email": "pietro46@example.org"
},
...
],
"paginatorInfo": {
"currentPage": 1,
"hasMorePages": true,
"lastPage": 10
}
}
}
}

For adding our search engine, we have to edit our graphql schema (by default in ./graphl/schema.graphql) to add the “search” directives to the users Query :

type Query {
users(search: String @search): [User!]! @paginate(defaultCount: 10)
}

And try it (the double quotes is mandatory, no single quote !):

POST http://localhost/graphql{
users(search: "Alan") {
data {
name
email
}
paginatorInfo {
currentPage
hasMorePages
lastPage
}
}
}

This will return all User with Alan in this name or email, depending or your Index determined in your User::toSearchableArray() methods!

That’s it, it’s a really efficient starting point to build our GraphQL API ! If you really don’t know GraphQL and except to know more about it, take a look a the php lighthouse documentation : https://lighthouse-php.com/master/api-reference/directives.html.

--

--