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

Bolt Laravel Package

stefanak-michal/bolt

Low-level PHP library for communicating with graph databases over TCP using the Bolt protocol. Works with Neo4j, Memgraph, Amazon Neptune and others, supports Bolt up to version 6, requires PHP 8.1+ with mbstring, and needs sockets or openssl only for certain connection classes.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require stefanak-michal/bolt
    

    Note (v7.5.0): The internal autoload.php was removed. Composer's autoloader is the only supported path going forward — ensure vendor/autoload.php is loaded if you're not on a framework.

  2. Basic connection:

    use Bolt\Bolt;
    use Bolt\connection\Socket;
    
    $connection = new Socket('localhost', 7687); // Default Neo4j Bolt port
    $bolt = new Bolt($connection);
    $protocol = $bolt->build(); // Auto-selects latest supported version (4.3-6.1)
    
  3. First query:

    $protocol
        ->hello()
        ->logon(['scheme' => 'basic', 'principal' => 'neo4j', 'credentials' => 'password'])
        ->run('MATCH (n) RETURN n LIMIT 10')
        ->pullAll();
    

Key Starting Points

  • Bolt class: Factory for protocol versions (default: 4.3-6.1). Prefer this entry point — internal constructor signatures changed in v7.5.0.
  • Socket/StreamSocket: Connection classes (use Socket for plain TCP, StreamSocket for SSL).
  • Client helper: Simplified CRUD operations (see Implementation Patterns).
  • PSR-3 Logger: Wire in any PSR-3 logger for message-level debugging (replaces the removed internal debug mechanism).

Implementation Patterns

Core Workflow

  1. Connection & Handshake:

    $protocol = (new Bolt(new Socket('host', 7687)))
        ->setProtocolVersions(5.4) // Optional: Force specific version
        ->build();
    $protocol->hello()->getResponse();
    
  2. Query Execution:

    // Pipeline pattern (recommended for performance)
    $protocol
        ->run('MATCH (n) WHERE n.name = $name RETURN n', ['name' => 'Alice'])
        ->pullAll(); // Fetch all records at once
    
  3. Transactions:

    $protocol
        ->begin()
        ->run('CREATE (n:Test {id: $id})', ['id' => 1])
        ->commit();
    
  4. Bookmarks (new in v7.5.0):

    // Capture a bookmark from a committed transaction and pass it forward
    $bookmark = $protocol->commit()->getResponse()->getBookmark();
    
    $second = (new Bolt(new Socket('host2', 7687)))->build();
    $second->hello()->logon([...]);
    $second->run('MATCH (n) RETURN n', [], ['bookmarks' => [$bookmark]]);
    

    Use bookmarks to enforce causal consistency across multiple connections (e.g. read-your-writes across a cluster).

Integration Tips

  • Laravel Service Provider:

    // config/bolt.php
    'connections' => [
        'neo4j' => [
            'host'    => env('NEO4J_HOST', 'localhost'),
            'port'    => env('NEO4J_PORT', 7687),
            'user'    => env('NEO4J_USER', 'neo4j'),
            'pass'    => env('NEO4J_PASS', 'password'),
            'version' => '5.4',
        ],
    ];
    
    // app/Providers/BoltServiceProvider.php
    public function register()
    {
        $this->app->singleton('bolt', function ($app) {
            $config = config('bolt.connections.neo4j');
            $connection = new Socket($config['host'], $config['port']);
            $bolt = (new Bolt($connection))->setProtocolVersions($config['version']);
    
            // v7.5.0: attach a PSR-3 logger for structured debugging
            $bolt->setLogger($app['log']);
    
            return $bolt->build();
        });
    }
    
  • Query Builder Helper:

    // app/Services/Neo4jQueryBuilder.php
    class Neo4jQueryBuilder {
        protected $protocol;
    
        public function __construct($protocol) {
            $this->protocol = $protocol;
        }
    
        public function findNodes(string $label, array $conditions = []) {
            $query = "MATCH (n:$label)";
            if (!empty($conditions)) {
                $query .= ' WHERE ' . $this->buildConditions($conditions);
            }
            $query .= ' RETURN n';
    
            return $this->protocol
                ->run($query, $conditions)
                ->pullAll();
        }
    }
    
  • Event Listeners:

    // Listen for model events (e.g., Eloquent)
    public function handle(NodeCreated $event) {
        $bolt = app('bolt');
        $bolt->run('CREATE (n:User {email: $email})', ['email' => $event->user->email]);
    }
    

Performance Patterns

  • Batching:

    // Process large datasets in chunks
    $protocol->run('MATCH (n) RETURN n SKIP $skip LIMIT 100', ['skip' => 0]);
    while (true) {
        $records = $protocol->pull()->getResponse();
        if (empty($records)) break;
        // Process records...
        $protocol->discard(); // Free server memory
        $protocol->run('... SKIP $skip LIMIT 100', ['skip' => $skip += 100]);
    }
    
  • Parameterized Queries:

    // Avoid string concatenation for security/performance
    $protocol->run(
        'MATCH (n:User {email: $email}) RETURN n',
        ['email' => $user->email]
    );
    

Gotchas and Tips

Common Pitfalls

  1. Protocol Version Mismatch:

    • Issue: Using Bolt 6.1 against Neo4j 4.x will fail during the handshake.
    • Fix: Explicitly set supported versions:
      $bolt->setProtocolVersions(4.4, 5.0); // Only allow these
      
  2. setPackStreamVersion() is deprecated (v7.5.0):

    • The method still works but is now a no-op warning path. Bolt 6.1 clarified that the PackStream version is coupled to the Bolt version — do not override it manually.
    • Remove any $bolt->setPackStreamVersion(...) calls from your bootstrap; let the auto-negotiated Bolt version decide the PackStream version.
  3. Custom AProtocol constructors will break:

    • v7.5.0 changed the constructor arguments for AProtocol. This is not a breaking change for normal consumers who get a protocol instance from Bolt::build(), but it is for anyone extending AProtocol directly.
    • Fix: Only subclass AProtocol for actual protocol extensions; retrieve instances via Bolt::build() in app code.
  4. Connection Timeouts:

    • Issue: Default socket timeouts may be too short for large queries.
    • Fix: Extend the Socket class or use StreamSocket with custom timeout:
      $socket = new StreamSocket('ssl://host:7687', [
          'timeout' => 30, // 30 seconds
      ]);
      
  5. Memory Leaks with Large Results:

    • Issue: pullAll() loads all records into memory.
    • Fix: Use pull() in a loop with discard():
      $protocol->run('MATCH (n) RETURN n');
      while (true) {
          $records = $protocol->pull()->getResponse();
          if (empty($records)) break;
          // Process records...
          $protocol->discard();
      }
      
  6. Authentication Errors:

    • Issue: Incorrect logon scheme/credentials.
    • Fix: Verify the scheme (e.g., basic, bearer) and inspect the response:
      $response = $protocol
          ->logon(['scheme' => 'basic', 'principal' => 'neo4j', 'credentials' => 'password'])
          ->getResponse();
      
  7. Unsupported Type handling (v7.5.0):

    • The new UnsupportedType structure means unknown Bolt types no longer crash pack/unpack — they surface as UnsupportedType envelopes.
    • Tip: When you spot a UnsupportedType in a record, check the Bolt version being negotiated — it usually means the server understands a type your protocol version doesn't.
  8. Cypher Parameter Types:

    • Issue: Passing wrong types (e.g., array instead of object for dictionaries).
    • Fix: Use Bytes class for binary data or implement IPackListGenerator for custom types.

Debugging Tips

  • Attach a PSR-3 logger (v7.5.0 replaces the old Bolt::$debug toggle):
    use Monolog\Logger;
    use Monolog\Handler\StreamHandler;
    
    $log = new Logger('bolt');
    $log->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
    
    $bolt->setLogger($log);          // or inject via Laravel's `$app['log']`
    $bolt->build();
    
  • Inspect Responses:
    $response = $protocol->getResponse();
    if ($response->isSuccess()) {
        // Handle success
    } else {
        throw new \RuntimeException($response->getFailureMessage());
    }
    
  • Check Server Logs: Neo4j logs Bolt protocol messages at DEBUG level. Enable in neo4j.conf:
    dbms.logs.query.debug=true
    

Extension Points

  1. Custom Connection Classes:

    • Extend AbstractConnection to support custom protocols (e.g., WebSocket):
      class WebSocketConnection extends AbstractConnection {
          public function connect() { /* ... */ }
      }
      
  2. Protocol Extensions:

    • Add custom Bolt messages by extending AbstractProtocol. Be aware that the parent constructor signature changed in v7.5.0 — mirror the current signature when overriding __construct:
      class CustomProtocol extends AbstractProtocol {
          public function customMessage(array $extra = []) {
              return $this->sendMessage('+Custom', $extra);
          }
      }
      
  3. Response Decorators:

    • Wrap Response objects to transform data:
      $response = $protocol->run('...')->pull()->getResponse();
      $nodes = collect($response->getRecords())->map(function ($record) {
          return (object) $record['n'];
      });
      

Configuration Quirks

  • PackStream Version — removed from your checklist:
    • v7.5.0 deprecates setPackStreamVersion(). The Bolt version determines the PackStream version (Bolt 6.1 pairs with the newer PackStream). Do not pin PackStream independently any more.
  • Bookmarks:
    • Supported natively for causal chaining across sessions. Pass them through the extra $extra/bookmark parameter on run() and read them via getBookmark() on the commit() / run() response.
  • Pipeline Limits:
    • Neo4j may reject pipelines with >1000 messages. Monitor logger output for PipelineTooLarge.
  • SSL/TLS:
    • Ensure openssl extension is enabled for StreamSocket with SSL:
      $socket = new StreamSocket('ssl://host:7687', [
          'ssl' => [
              'verify_peer' => false, // Disable for self-signed certs (not recommended)
          ],
      ]);
      

Performance Gotchas

  • Large Transactions:
    • Avoid long-running transactions (>5 minutes). Use commit()/rollback() frequently.
  • Parameter Binding:
    • Bind parameters after the query string to avoid syntax errors:
      // Correct
      $protocol->run('MATCH (n) WHERE n.id = $id', ['id' => 1]);
      
      // Incorrect (syntax error)
      $protocol
      
  • Causal Consistency Cost:
    • Bookmarked queries serialize through cluster routing — only use bookmarks when read-your-writes matters; skip them for pure read-only dashboards where speed is the priority.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource