$npx -y skills add neo4j-contrib/neo4j-skills --skill neo4j-driver-go-skillCovers the Neo4j Go Driver v6 — driver lifecycle, ExecuteQuery, managed and
| 1 | ## When to Use |
| 2 | - Writing Go code that connects to Neo4j |
| 3 | - Setting up `neo4j.NewDriver()`, `ExecuteQuery()`, or session/transaction patterns |
| 4 | - Debugging connection errors, result iteration, type assertions, causal consistency |
| 5 | |
| 6 | ## When NOT to Use |
| 7 | - **Writing/optimizing Cypher** → `neo4j-cypher-skill` |
| 8 | - **v5→v6 migration steps** → `neo4j-migration-skill` |
| 9 | |
| 10 | --- |
| 11 | |
| 12 | ## Installation |
| 13 | |
| 14 | ```bash |
| 15 | go get github.com/neo4j/neo4j-go-driver/v6 |
| 16 | ``` |
| 17 | |
| 18 | Import: `github.com/neo4j/neo4j-go-driver/v6/neo4j` |
| 19 | |
| 20 | **v5→v6 rename** (deprecated aliases still compile, remove before v7): |
| 21 | |
| 22 | | v5 | v6 | |
| 23 | |----|----| |
| 24 | | `neo4j.NewDriverWithContext(...)` | `neo4j.NewDriver(...)` | |
| 25 | | `neo4j.DriverWithContext` | `neo4j.Driver` | |
| 26 | |
| 27 | --- |
| 28 | |
| 29 | ## Environment Variables |
| 30 | |
| 31 | ```go |
| 32 | import "os" |
| 33 | |
| 34 | uri := getEnv("NEO4J_URI", "neo4j://localhost:7687") |
| 35 | user := getEnv("NEO4J_USERNAME", "neo4j") |
| 36 | password := getEnv("NEO4J_PASSWORD", "") |
| 37 | database := getEnv("NEO4J_DATABASE", "neo4j") |
| 38 | |
| 39 | func getEnv(key, fallback string) string { |
| 40 | if v := os.Getenv(key); v != "" { return v } |
| 41 | return fallback |
| 42 | } |
| 43 | ``` |
| 44 | |
| 45 | Use [godotenv](https://github.com/joho/godotenv) to load `.env` in dev: `godotenv.Load()`. `.env` in `.gitignore`. |
| 46 | |
| 47 | --- |
| 48 | |
| 49 | ## Driver Lifecycle |
| 50 | |
| 51 | One `Driver` per application. Goroutine-safe, connection-pooled, expensive to create. |
| 52 | |
| 53 | ```go |
| 54 | func NewNeo4jDriver(uri, user, password string) (neo4j.Driver, error) { |
| 55 | driver, err := neo4j.NewDriver( |
| 56 | uri, // "neo4j+s://xxx.databases.neo4j.io" for Aura |
| 57 | neo4j.BasicAuth(user, password, ""), |
| 58 | ) |
| 59 | if err != nil { |
| 60 | return nil, fmt.Errorf("create driver: %w", err) |
| 61 | } |
| 62 | ctx := context.Background() |
| 63 | if err := driver.VerifyConnectivity(ctx); err != nil { |
| 64 | driver.Close(ctx) |
| 65 | return nil, fmt.Errorf("verify connectivity: %w", err) |
| 66 | } |
| 67 | return driver, nil |
| 68 | } |
| 69 | |
| 70 | // In main / app teardown: |
| 71 | defer driver.Close(ctx) |
| 72 | ``` |
| 73 | |
| 74 | ❌ Never create driver per-request. Create once at startup; share across goroutines. |
| 75 | |
| 76 | URI schemes: `neo4j+s://` (Aura/TLS+routing), `neo4j://` (plain+routing), `bolt+s://` (TLS+single), `bolt://` (plain+single). |
| 77 | |
| 78 | --- |
| 79 | |
| 80 | ## Choosing the Right API |
| 81 | |
| 82 | | API | Use when | Auto-retry | Lazy results | |
| 83 | |-----|----------|:----------:|:------------:| |
| 84 | | `neo4j.ExecuteQuery()` | Most queries — simple default | ✅ | ❌ eager | |
| 85 | | `session.ExecuteRead/Write()` | Large result sets / streaming | ✅ | ✅ | |
| 86 | | `session.BeginTransaction()` | Spans multiple functions / ext coordination | ❌ | ✅ | |
| 87 | | `session.Run()` | `CALL IN TRANSACTIONS` / auto-commit only | ❌ | ✅ | |
| 88 | |
| 89 | `CALL { … } IN TRANSACTIONS` and `USING PERIODIC COMMIT` manage their own transactions — use `session.Run()`. They fail inside managed transactions. |
| 90 | |
| 91 | --- |
| 92 | |
| 93 | ## ExecuteQuery (Recommended Default) |
| 94 | |
| 95 | Manages sessions, transactions, retries, and bookmarks automatically. |
| 96 | |
| 97 | ```go |
| 98 | result, err := neo4j.ExecuteQuery(ctx, driver, |
| 99 | `MATCH (p:Person {name: $name})-[:KNOWS]->(friend) |
| 100 | RETURN friend.name AS name`, |
| 101 | map[string]any{"name": "Alice"}, |
| 102 | neo4j.EagerResultTransformer, |
| 103 | neo4j.ExecuteQueryWithDatabase("neo4j"), // always specify |
| 104 | neo4j.ExecuteQueryWithReadersRouting(), // for read queries |
| 105 | ) |
| 106 | if err != nil { |
| 107 | return fmt.Errorf("query people: %w", err) |
| 108 | } |
| 109 | |
| 110 | for _, record := range result.Records { |
| 111 | name, _ := record.Get("name") |
| 112 | fmt.Println(name) |
| 113 | } |
| 114 | fmt.Println(result.Summary.Counters().NodesCreated()) |
| 115 | ``` |
| 116 | |
| 117 | Key options: |
| 118 | ```go |
| 119 | neo4j.ExecuteQueryWithDatabase("mydb") // required for performance |
| 120 | neo4j.ExecuteQueryWithReadersRouting() // route reads to replicas |
| 121 | neo4j.ExecuteQueryWithImpersonatedUser("jane") // impersonate |
| 122 | neo4j.ExecuteQueryWithoutBookmarkManager() // opt out of causal consistency |
| 123 | ``` |
| 124 | |
| 125 | ❌ Never concatenate user input into query strings. Always use `map[string]any` parameters. |
| 126 | |
| 127 | --- |
| 128 | |
| 129 | ## Managed Transactions (Session-Based) |
| 130 | |
| 131 | Use for lazy streaming (large result sets) or callback-level control. |
| 132 | |
| 133 | ```go |
| 134 | session := driver.NewSession(ctx, neo4j.SessionConfig{ |
| 135 | DatabaseName: "neo4j", // always specify |
| 136 | AccessMode: neo4j.AccessModeRead, |
| 137 | }) |
| 138 | defer session.Close(ctx) |
| 139 | |
| 140 | result, err := session.ExecuteRead(ctx, |
| 141 | func(tx neo4j.ManagedTransaction) (any, error) { |
| 142 | res, err := tx.Run(ctx, |
| 143 | `MATCH (p:Person) RETURN p.name AS name LIMIT $limit`, |
| 144 | map[string]any{"limit": 100}, |
| 145 | ) |