From 445a3d0ce4bf556431f549cabfd4871f48fb3fc5 Mon Sep 17 00:00:00 2001 From: stewartmbofana Date: Tue, 14 Oct 2025 08:40:22 +0200 Subject: [PATCH 1/3] CHore: Optimize Neo4J Recipe --- neo4j/README.md | 40 +++++++++------------ neo4j/main.go | 95 +++++++++++++++++++++++++------------------------ 2 files changed, 66 insertions(+), 69 deletions(-) diff --git a/neo4j/README.md b/neo4j/README.md index 52979e247e..516f895a16 100644 --- a/neo4j/README.md +++ b/neo4j/README.md @@ -22,21 +22,24 @@ Ensure you have the following installed: ## Setup 1. Clone the repository: - ```sh - git clone https://github.com/gofiber/recipes.git - cd recipes/neo4j - ``` + + ```sh + git clone https://github.com/gofiber/recipes.git + cd recipes/neo4j + ``` 2. Install dependencies: - ```sh - go get - ``` + + ```sh + go get + ``` 3. Set up your Neo4j database and update the connection string in the code. ## Running the Application 1. Start the application: + ```sh go run main.go ``` @@ -50,42 +53,33 @@ package main import ( "log" + "net/http" "github.com/gofiber/fiber/v2" "github.com/neo4j/neo4j-go-driver/v5/neo4j" ) func main() { - // Neo4j connection - uri := "neo4j://localhost:7687" - username := "neo4j" - password := "password" - driver, err := neo4j.NewDriver(uri, neo4j.BasicAuth(username, password, "")) + driver, err := neo4j.NewDriver("neo4j://localhost:7687", neo4j.BasicAuth("neo4j", "password", "")) if err != nil { log.Fatal(err) } - defer driver.Close() - - // Fiber instance + defer driver.Close() app := fiber.New() - // Routes app.Get("/", func(c *fiber.Ctx) error { session := driver.NewSession(neo4j.SessionConfig{}) - defer session.Close() - + defer session.Close() result, err := session.Run("RETURN 'Hello, World!'", nil) if err != nil { - return err - } - + return c.Status(http.StatusInternalServerError).SendString(err.Error()) + } if result.Next() { return c.SendString(result.Record().Values[0].(string)) } - return c.SendStatus(500) + return c.SendStatus(http.StatusInternalServerError) }) - // Start server log.Fatal(app.Listen(":3000")) } ``` diff --git a/neo4j/main.go b/neo4j/main.go index bc41e3c0c0..bb9f9b57ba 100644 --- a/neo4j/main.go +++ b/neo4j/main.go @@ -1,9 +1,8 @@ package main import ( - "fmt" "log" - "strconv" + "net/http" "github.com/gofiber/fiber/v2" "github.com/neo4j/neo4j-go-driver/neo4j" @@ -17,74 +16,78 @@ type Movie struct { Director string `json:"director" db:"director"` } -// ConnectToDB makes a connection with the database -func ConnectToDB() (neo4j.Session, neo4j.Driver, error) { - // define driver, session and result vars - var ( - driver neo4j.Driver - session neo4j.Session - err error - ) - // initialize driver to connect to localhost with ID and password - if driver, err = neo4j.NewDriver("bolt://localhost:7687", neo4j.BasicAuth("mdfaizan7", "mdfaizan7", ""), - func(conf *neo4j.Config) { conf.Encrypted = false }); err != nil { - return nil, nil, err - } - // Open a new session with write access - if session, err = driver.Session(neo4j.AccessModeWrite); err != nil { - return nil, nil, err - } - return session, driver, nil +var driver neo4j.Driver + +func ConnectToDB() error { + var err error + driver, err = neo4j.NewDriver("bolt://localhost:7687", neo4j.BasicAuth("neo4j", "password", ""), + func(conf *neo4j.Config) { conf.Encrypted = false }) + return err } func main() { - // connect to database - session, _, err := ConnectToDB() - if err != nil { - fmt.Print(err) - session.Close() + if err := ConnectToDB(); err != nil { + log.Fatal(err) } - defer session.Close() + defer driver.Close() - // Create a Fiber app app := fiber.New() app.Post("/movie", func(c *fiber.Ctx) error { - movie := new(Movie) - if err := c.BodyParser(movie); err != nil { - return c.Status(400).SendString(err.Error()) + var movie Movie + if err := c.BodyParser(&movie); err != nil { + return c.Status(http.StatusBadRequest).SendString(err.Error()) + } + + session, err := driver.Session(neo4j.AccessModeWrite) + if err != nil { + return c.Status(http.StatusInternalServerError).SendString(err.Error()) } + defer session.Close() - query := fmt.Sprintf(`CREATE (n:Movie {title:'%s', tagline:'%s', released:'%d', director:'%s' })`, - movie.Title, movie.Tagline, movie.Released, movie.Director) + query := `CREATE (n:Movie {title: $title, tagline: $tagline, released: $released, director: $director})` + params := map[string]any{ + "title": movie.Title, + "tagline": movie.Tagline, + "released": movie.Released, + "director": movie.Director, + } - _, err := session.Run(query, nil) + _, err = session.Run(query, params) if err != nil { - return err + return c.Status(http.StatusInternalServerError).SendString(err.Error()) } - return c.SendString("Movie successfully created") + return c.Status(http.StatusCreated).JSON(movie) }) app.Get("/movie/:title", func(c *fiber.Ctx) error { title := c.Params("title") - query := fmt.Sprintf(`MATCH (n:Movie {title:'%s'}) RETURN n.title, n.tagline, n.released, n.director`, title) - result, err := session.Run(query, nil) + session, err := driver.Session(neo4j.AccessModeRead) + if err != nil { + return c.Status(http.StatusInternalServerError).SendString(err.Error()) + } + defer session.Close() + + query := `MATCH (n:Movie {title: $title}) RETURN n.title, n.tagline, n.released, n.director` + result, err := session.Run(query, map[string]interface{}{"title": title}) if err != nil { - return err + return c.Status(http.StatusInternalServerError).SendString(err.Error()) } - res := &Movie{} - for result.Next() { - record := result.Record() - res.Title = record.GetByIndex(0).(string) - res.Tagline = record.GetByIndex(1).(string) - res.Released, _ = strconv.ParseInt(record.GetByIndex(2).(string), 10, 64) - res.Director = record.GetByIndex(3).(string) + if result.Next() { + values := result.Record().Values() + movie := Movie{ + Title: values[0].(string), + Tagline: values[1].(string), + Released: values[2].(int64), + Director: values[3].(string), + } + return c.JSON(movie) } - return c.JSON(res) + return c.SendStatus(http.StatusNotFound) }) log.Fatal(app.Listen(":3000")) From f00a7e00f28ff2868da81950dc480697afb67cce Mon Sep 17 00:00:00 2001 From: stewartmbofana Date: Tue, 14 Oct 2025 09:29:02 +0200 Subject: [PATCH 2/3] Review changes --- neo4j/go.mod | 7 ++----- neo4j/go.sum | 43 ++----------------------------------------- neo4j/main.go | 28 +++++++--------------------- 3 files changed, 11 insertions(+), 67 deletions(-) diff --git a/neo4j/go.mod b/neo4j/go.mod index 52132fad84..9efd63cc33 100644 --- a/neo4j/go.mod +++ b/neo4j/go.mod @@ -2,10 +2,7 @@ module neo4j go 1.23.0 -require ( - github.com/gofiber/fiber/v2 v2.52.9 - github.com/neo4j/neo4j-go-driver v1.8.3 -) +require github.com/gofiber/fiber/v2 v2.52.9 require ( github.com/andybalholm/brotli v1.2.0 // indirect @@ -14,9 +11,9 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/neo4j/neo4j-go-driver/v5 v5.28.4 github.com/rivo/uniseg v0.4.4 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.64.0 // indirect golang.org/x/sys v0.34.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/neo4j/go.sum b/neo4j/go.sum index 484dee44c9..db6e4c89f7 100644 --- a/neo4j/go.sum +++ b/neo4j/go.sum @@ -1,14 +1,9 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -18,15 +13,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/neo4j/neo4j-go-driver v1.8.3 h1:yfuo9YBAlezdIiogu92GwEir/81RD81dNwS5mY/wAIk= -github.com/neo4j/neo4j-go-driver v1.8.3/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/neo4j/neo4j-go-driver/v5 v5.28.4 h1:7toxehVcYkZbyxV4W3Ib9VcnyRBQPucF+VwNNmtSXi4= +github.com/neo4j/neo4j-go-driver/v5 v5.28.4/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -36,34 +24,7 @@ github.com/valyala/fasthttp v1.64.0 h1:QBygLLQmiAyiXuRhthf0tuRkqAFcrC42dckN2S+N3 github.com/valyala/fasthttp v1.64.0/go.mod h1:dGmFxwkWXSK0NbOSJuF7AMVzU+lkHz0wQVvVITv2UQA= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/neo4j/main.go b/neo4j/main.go index bb9f9b57ba..3a2de2b496 100644 --- a/neo4j/main.go +++ b/neo4j/main.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/gofiber/fiber/v2" - "github.com/neo4j/neo4j-go-driver/neo4j" + "github.com/neo4j/neo4j-go-driver/v5/neo4j" ) // Movie represent the movie schema @@ -16,17 +16,9 @@ type Movie struct { Director string `json:"director" db:"director"` } -var driver neo4j.Driver - -func ConnectToDB() error { - var err error - driver, err = neo4j.NewDriver("bolt://localhost:7687", neo4j.BasicAuth("neo4j", "password", ""), - func(conf *neo4j.Config) { conf.Encrypted = false }) - return err -} - func main() { - if err := ConnectToDB(); err != nil { + driver, err := neo4j.NewDriver("neo4j://localhost:7687", neo4j.BasicAuth("neo4j", "password", "")) + if err != nil { log.Fatal(err) } defer driver.Close() @@ -39,10 +31,7 @@ func main() { return c.Status(http.StatusBadRequest).SendString(err.Error()) } - session, err := driver.Session(neo4j.AccessModeWrite) - if err != nil { - return c.Status(http.StatusInternalServerError).SendString(err.Error()) - } + session := driver.NewSession(neo4j.SessionConfig{DatabaseName: "movies", AccessMode: neo4j.AccessModeWrite}) defer session.Close() query := `CREATE (n:Movie {title: $title, tagline: $tagline, released: $released, director: $director})` @@ -64,20 +53,17 @@ func main() { app.Get("/movie/:title", func(c *fiber.Ctx) error { title := c.Params("title") - session, err := driver.Session(neo4j.AccessModeRead) - if err != nil { - return c.Status(http.StatusInternalServerError).SendString(err.Error()) - } + session := driver.NewSession(neo4j.SessionConfig{DatabaseName: "movies", AccessMode: neo4j.AccessModeRead}) defer session.Close() query := `MATCH (n:Movie {title: $title}) RETURN n.title, n.tagline, n.released, n.director` - result, err := session.Run(query, map[string]interface{}{"title": title}) + result, err := session.Run(query, map[string]any{"title": title}) if err != nil { return c.Status(http.StatusInternalServerError).SendString(err.Error()) } if result.Next() { - values := result.Record().Values() + values := result.Record().Values movie := Movie{ Title: values[0].(string), Tagline: values[1].(string), From 1b975eeb27cc13922f016c85c3398e2997c288af Mon Sep 17 00:00:00 2001 From: stewartmbofana Date: Thu, 16 Oct 2025 08:31:12 +0200 Subject: [PATCH 3/3] Review changes --- neo4j/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/neo4j/main.go b/neo4j/main.go index 3a2de2b496..efaafb37a4 100644 --- a/neo4j/main.go +++ b/neo4j/main.go @@ -72,7 +72,10 @@ func main() { } return c.JSON(movie) } - + if err = result.Err(); err != nil { + return c.Status(http.StatusInternalServerError).SendString(err.Error()) + } + return c.SendStatus(http.StatusNotFound) })