Stop relying on slow database LIKE queries. Learn how to integrate search drivers, index Eloquent models, and build a high-performance Search API in Laravel.
Previously in this course, we explored Database Indexing Strategies to speed up standard relational queries. While database indexes are essential for lookups, they struggle with full-text search requirements like fuzzy matching, relevance scoring, and multi-field weighted results.
In this lesson, we will move beyond WHERE title LIKE '%query%' and implement a dedicated search layer using Laravel Scout. This allows us to integrate powerful search engines like Meilisearch or Algolia, providing a fast, scalable search experience for our project board.
When building a search API, we decouple the storage of data from the retrieval of data. Our primary database remains the source of truth, but we maintain a secondary "search index" optimized for high-speed retrieval.
| Feature | Standard Database Query | Dedicated Search Engine |
|---|---|---|
| Speed | Slow on large datasets | Near-instant |
| Typo Tolerance | None | High |
| Relevance | Basic order by | Weighted scoring |
| Complexity | High (complex SQL) | Simple (API-based) |
We will use Laravel Scout, the official driver-based search abstraction. First, install the package and the Meilisearch engine driver:
Bashcomposer require laravel/scout meilisearch/meilisearch-php php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
Next, configure your .env file to use the driver:
.envSCOUT_DRIVER=meilisearch MEILISEARCH_HOST=http://127.0.0.1:7700
To make your Task model searchable, add the Laravel\Scout\Searchable trait. This trait hooks into model events—automatically syncing data to the search index whenever a record is created, updated, or deleted.
PHPnamespace App\Models; use Illuminate\Database\Eloquent\Model; use Laravel\Scout\Searchable; class Task extends Model { use Searchable; #6A9955">// Define which data goes into the index public function toSearchableArray(): array { return [ 'id' => $this->id, 'title' => $this->title, 'description' => $this->description, 'status' => $this->status, ]; } }
Now, run the import command to push your existing database records into the search engine:
Bashphp artisan scout:import "App\Models\Task"
With the infrastructure in place, we can now create a clean endpoint. We’ll inject a SearchService (following the patterns established in our Service Layer lesson) to handle the search logic.
PHP#6A9955">// app/Http/Controllers/Api/TaskSearchController.php public function index(Request $request) { $query = $request->input('q'); #6A9955">// Perform the search $tasks = Task::search($query) ->where('status', 'active') #6A9955">// Filter by attribute ->paginate(15); return TaskResource::collection($tasks); }
toSearchableArray method to include a project_name key by loading the project relationship.Searchable triggers on model events. In high-traffic apps, consider setting SCOUT_QUEUE=true to offload the indexing work to your background queues, as discussed in Asynchronous Processing with Queues.Searchable trait to an existing model, don't forget to run scout:import. New records will sync automatically, but old ones won't appear until you import them.We’ve successfully decoupled our search logic from the primary database, integrated a professional search driver, and created a responsive API endpoint. By leveraging Scout, our project board can now handle complex, relevant searches without sacrificing performance.
Up next: We will address data integrity during high-load scenarios by Handling Concurrency and Race Conditions.
Master API versioning and maintain backward compatibility in your distributed systems. Learn to implement header-based versioning for clean, scalable APIs.
Read moreMaster stateless API authentication in Laravel. Learn to issue and verify JWTs, implement secure token rotation, and handle revocation in a high-traffic system.
Building a Search API