Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Scout Typesense Driver Laravel Package

typesense/laravel-scout-typesense-driver

A Typesense driver for Laravel Scout that brings fast, typo-tolerant full-text search, faceting, filtering, geo search, and synonyms to Eloquent models. Swap Scout's default engine for Typesense with simple configuration and a native Laravel integration for scalable, relevant app search.

View on GitHub
Deep Wiki
Context7

Getting Started

The driver plugs Typesense into Laravel Scout, so Scout is the mental model — this package only supplies the engine adapter plus a richer Searchable trait. Read the source of Typesense\LaravelTypesense\Typesense (the engine) and the README's schema section first; those two files explain 90% of runtime behavior.

Minimal path:

  1. composer require typesense/laravel-scout-typesense-driver
  2. Run Typesense locally:
    docker run -p 8108:8108 -v/tmp/typesense-data:/data typesense/typesense:27.1 \
      --data-dir /data --api-key=xyz --enable-cors
    curl http://localhost:8108/health   # {"ok":true}
    
  3. Configure Scout in .env and config/scout.php:
    'driver' => env('SCOUT_DRIVER', 'typesense'),
    'typesense' => [
        'api_key' => env('TYPESENSE_API_KEY', 'xyz'),
        'nodes' => [[
            'host'     => env('TYPESENSE_HOST', 'localhost'),
            'port'     => env('TYPESENSE_PORT', '8108'),
            'protocol' => env('TYPESENSE_PROTOCOL', 'http'),
        ]],
        'connection_timeout' => env('TYPESENSE_CONNECTION_TIMEOUT', 2),
    ],
    
  4. Make a model searchable:
    use Laravel\Scout\Searchable;
    
    class Post extends Model
    {
        use Searchable;
    
        public function toSearchableArray(): array
        {
            return [
                'id'          => (string) $this->id,
                'title'       => $this->title,
                'body'        => $this->body,
                'likes'       => (int) $this->likes,
                'published'   => (bool) $this->published,
                'created_at'  => $this->created_at->timestamp,
            ];
        }
    }
    
  5. php artisan scout:import "App\Models\Post"
  6. Post::search('laravel')->get();

If step 6 returns nothing, check curl 'http://localhost:8108/collections' — an empty list almost always means the import failed silently on a schema/type mismatch.

Implementation Patterns

Define the schema explicitly. Scout's derived schema is fine for demos, but the driver's real value is control over the Typesense collection:

public function getCollectionSchema(): array
{
    return [
        'name' => $this->searchableAs(),          // defaults to table name
        'fields' => [
            ['name' => 'title',   'type' => 'string'],
            ['name' => 'body',    'type' => 'string', 'index' => false],
            ['name' => 'likes',   'type' => 'int32'],
            ['name' => 'tags',    'type' => 'string[]', 'facet' => true],
            ['name' => 'location','type' => 'geopoint'],
            ['name' => 'created_at', 'type' => 'int64'],
        ],
        'default_sorting_field' => 'likes',
    ];
}

(Older versions accepted a Typesense\Collection $collection argument — check the method signature on your installed version before overriding.)

Query options are the main levers. Everything Scout doesn't model natively is passed through ->options():

Post::search('laravel')
    ->options([
        'query_by'         => 'title,body',
        'query_by_weights' => '4,1',
        'filter_by'        => 'published:=true && likes:>10',
        'sort_by'          => 'likes:desc',
        'facet_by'         => 'tags',
        'num_typos'        => 1,
        'prefix'           => true,
    ])
    ->paginate(15);

Geo search — declare a geopoint field, store [lat, lng], then filter:

->options(['filter_by' => 'location:(48.85, 2.34, 10 km)'])

Vector / semantic search — declare an float[] field (typically embedding) and pass a vector_query through ->options(). Keep embedding generation outside the driver; it only transports the vector.

Queued indexing. In production set SCOUT_QUEUE=true and give Scout its own queue connection so a slow Typesense node doesn't stall mail or webhooks. Use toSearchableArray() as the only place that shapes index data — never mutate models inside shouldBeSearchable().

Multi-tenancy by overriding searchableAs() per tenant ("posts_{$tenant->id}"), and drive index creation with php artisan scout:import in tenant provisioning jobs.

Raw admin operations go through the facade rather than Scout:

use Typesense\LaravelTypesense\Facades\Typesense;

Typesense::getCollections();                       // list
Typesense::getCollection('posts')->delete();
Typesense::multiSearch(['searches' => [...]]);    // multi-collection search

Synonyms, curations, and API-key management are not covered by the driver — manage those through the Typesense client or dashboard.

Testing. Set SCOUT_DRIVER=null (or collection) in phpunit.xml so tests never hit a live Typesense. If you need assertion-level coverage, spin up Typesense in CI via the Docker image and import fixtures in setUp.

Gotchas and Tips

  • Typesense is strictly typed. toSearchableArray() must return int for int32, bool for bool, arrays for [] fields. A numeric string like "42" fails the import with an opaque 422. Cast explicitly.
  • Nulls break indexing. Typesense drops null values; if a field is required downstream, coalesce to '' / 0 / [] in toSearchableArray().
  • Schema changes are not migrated. scout:import creates the collection if missing but won't alter an existing one. The reliable routine is php artisan scout:flush "App\Models\Post" then re-import — or use scout:sync-index-settings if your version supports it (settings only, not fields).
  • default_sorting_field must be a non-optional numeric field and must also exist in every document, otherwise indexing fails.
  • Result ordering. Scout hydrates Eloquent models by ID; relevance order is preserved by this driver, but any whereIn-style post-filtering in your own code will scramble it. If order matters, assert on $results->pluck('id') in tests.
  • Chunked imports and memory. For large tables, scout:import will happily exhaust memory on toSearchableArray() if you lazy-load relations. Eager-load them or use makeSearchableUsing() / searchable() in batches.
  • Soft deletes. The driver maintains a __soft_deleted flag; when querying a soft-deletable model, add 'filter_by' => '__soft_deleted:=0' explicitly if your results look inflated.
  • Key confusion. TYPESENSE_API_KEY should be the admin key for indexing. If you later switch the search path to a search-only key, indexing jobs must keep the admin key — split the config rather than swapping the env var globally.
  • Connection timeouts surface as silent empty results when connection_timeout is low and the node is cold. Bump it to 5–10s in development, and always curl /health before debugging Laravel code.
  • Debugging queries. Wrap calls in a try/catch and log the exception message — the Typesense client's error bodies include field and error keys that pinpoint the offending document. Typesense::getDebug() on the facade exposes the last request in the PHP client.
  • Version coupling. Driver releases track specific Typesense server majors (v27/28 at the time of the last release). Pin both in docker-compose and CI, and re-read the README before upgrading either.
  • Extension points are just overrides on the model: searchableAs(), toSearchableArray(), getCollectionSchema(), shouldBeSearchable(), and makeSearchableUsing(). If you need new engine behavior, extend Typesense\LaravelTypesense\Typesense and rebind Scout's engine in a service provider rather than forking the package.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo