Product Decisions This Supports
Feature and roadmap bets
- Decides whether graph-shaped features are worth building now: recommendations/similar users, fraud and abuse ring detection, org and permission hierarchies, dependency/impact graphs, knowledge graphs, feed ranking. If the roadmap contains two or more of these, this package is the enabling dependency; if it contains only one shallow "related items" feature, a relational table plus recursive CTE is cheaper.
- Informs the "graph vs. relational" call at the data-model level. Cypher-native traversal depth (variable-length paths,
MERGE for upserts) is the differentiator to test in a spike, not the driver itself.
- Flags a real product constraint early: the driver exposes Neo4j
Vector types on read but not as query parameters. Any "find similar by embedding" or semantic-search feature will need its vector inlined into the Cypher statement or generated server-side — a spec-level decision that affects query-builder design, parameter binding, and input-safety review. Better surfaced in discovery than in sprint 3.
Build vs. buy
- Buying is clearly correct on the client layer: implementing the Bolt protocol, cluster routing, bookmarks, and Aura compatibility in-house is months of work with no product differentiation. MIT license, no per-seat or runtime cost, no lock-in on the client side.
- What you are not buying: an Eloquent-style ODM, migrations, model lifecycle hooks, or a Laravel service provider. The package is a driver, and the README's Laravel example is a demo app, not a first-party integration. Budget the wrapper (provider, config, connection health check, test fakes) as your own deliverable — that's the true "build" portion of the buy decision.
Architecture commitments that become product commitments
- The driver's transaction functions auto-retry on transient errors and roll back on timeout. That makes them the standard for write paths — and it hard-requires idempotent work inside them. Product consequence: side effects like emails, queue pushes, webhooks, and analytics counters must live outside the transaction closure. This is a spec rule, not a style preference, and it needs to be written into the engineering definition of done before the first graph write ships.
- Choosing
bolt:// vs. neo4j:// auto-routed drivers is a deployment-tier decision with cost attached: a single Bolt target for dev/staging, auto-routing if you ever move to Aura or a cluster. Pick the topology you'll still be happy with at 10x traffic; retrofitting routing is a config-plus-testing exercise, not a code change.
- Result hydration into typed
CypherMap / CypherList / Node / Relationship objects shapes your API responses and your QA plan. Decide serialization at the boundary (do not leak driver types to clients) and it stays a non-issue.
Delivery and organisational leverage
- Full Psalm annotations and typed results reduce defect rate and onboarding time — a genuine velocity argument when justifying adoption to engineering leadership, and a screening criterion when hiring PHP engineers.
- Summarized results (counters, timings, query plan, notifications) give you observability and "why is this slow" answers without a separate APM investment for the graph path.
When to Consider This Package
Adopt when most of these hold
- Graph traversal is core to the value proposition, not incidental — multi-hop relationships, shortest path, pattern matching over heterogeneous edges.
- The stack is PHP 8.0+ and you run Neo4j 4.x/5.x or Aura. (PHP 7.4 pins you to
^2.8 and Neo4j 3.5/4.0 — check before committing.)
- You can install
ext-bcmath, ext-json, and ext-sockets in every environment, including CI and any managed host. ext-sockets is the one that quietly kills shared-hosting or locked-down PaaS deployments.
- You need cluster or Aura routing, bookmarks, or OIDC authentication — the matrix confirms all of these are supported.
- You're willing to own the Laravel integration layer (provider, config publishing, connection pooling assumptions, a test double for CI) and pin driver versions deliberately.
- You have at least one engineer who can read Cypher and reason about transaction semantics; the package is well-built but it is not a data-layer abstraction that hides the database.
Look elsewhere or defer when
- The need is genuinely relational: reporting, simple lookup hierarchies, or one-to-many associations. Postgres with recursive CTEs and
ltree will be cheaper to operate and hires for.
- You need an Eloquent-like ODM with models, relationships, and migrations. That's a different (and thinner, less maintained) category — evaluate it separately rather than assuming this package covers it.
- You need HTTP/HTTPS transport: removed in 3.3.0. Only Bolt-family (
bolt://, bolt+s://) and neo4j:// URIs remain. If a network policy blocks raw TCP to 7687, this package is not viable without infrastructure work.
- You require a vendor SLA, formal support contract, or security-review provenance. Stars (188) and public dependent count are thin ecosystem signals — not a quality verdict, but it means you are the support tier. Mitigate by pinning versions, adding contract tests against a real Neo4j instance in CI, and keeping the driver behind your own interface.
- Graph needs are purely analytical/ad-hoc. Use Neo4j Bloom, a BI connector, or notebook tooling; don't put a driver in the product for exploratory queries.
- The team has no appetite for an owned integration: if nobody will maintain the wrapper across Laravel and PHP upgrades, the maintenance debt outweighs the capability.
Recommended adoption gate: a timeboxed one-to-two-sprint spike — one engineer, a free Aura instance, three real queries from the backlog (one read-heavy traversal, one idempotent write transaction, one path/pattern query), plus the Laravel wrapper and a CI job running against a containerised Neo4j. Decide on measured latency and developer ergonomics, not on the README.
How to Pitch It (Stakeholders)
To executives — one paragraph
We're evaluating a graph capability for [feature area], and this is the connection layer that makes it possible in our existing PHP/Laravel stack. It's a small, MIT-licensed, fully typed client built in close collaboration with Neo4j's own driver team and validated against their official conformance suite — so it's a mature piece of plumbing rather than an experimental dependency, and there's no license or per-seat cost. The real cost isn't the package; it's the one-to-two sprints of engineering to integrate it properly and the ongoing ownership of a thin wrapper. That's why the ask is a timeboxed spike with a clear go/no-go, not a platform commitment: we test three real queries against our actual backlog, measure, and come back with a costed recommendation.
To engineering — the practical case
- Correctness is delegated, not reinvented: Bolt and auto-routed drivers, transactions with automatic retry on transient errors, rollback on timeout, bookmarks, cluster and Aura support, OIDC auth.
- Ergonomic where it matters: three call styles (auto-commit, transaction functions, unmanaged transactions), first-class
run/runStatement/runStatements, and typed result objects with a full Cypher→PHP type map.
- Typed end to end with Psalm annotations — better IDE completion, better static analysis, fewer runtime surprises.
- Escape hatches are built in: pluggable drivers, swappable result formatters,
ParameterHelper for the empty-list-vs-empty-map ambiguity, and result summaries with counters, timings, and query plans for debugging.
- Costs we accept knowingly: we write the Laravel service provider and config ourselves; we require
ext-bcmath/ext-json/ext-sockets in every image; only Bolt-family URIs are supported; and transaction closures must be idempotent — that's a coding standard we enforce in review.
- Risk posture: the driver sits behind our own interface, so replacement cost stays low if the ecosystem thins out.
The decision being asked for
Approve a two-sprint spike (one engineer, free-tier Aura dev instance) to deliver a working Laravel integration, three representative queries from the backlog, and a benchmarked go/no-go. Success criteria agreed up front: query latency at expected data volume, wrapper surface under ~200 lines, and a CI job that runs the graph path against a real Neo4j container on every PR.