> ## Documentation Index
> Fetch the complete documentation index at: https://docs.helix-db.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Go

> How to setup a Go project to use HelixDB

> For the complete documentation index optimized for AI agents, see [llms.txt](/llms.txt).

Queries for HelixDB can be authored directly in Go with the
`github.com/helixdb/helix-db/sdks/go` module. The Go SDK is dynamic-first: write
ordinary Go functions that return `helix.Request`, declare parameters inline, and
execute with `client.Exec(ctx, request, &out)`. There is no bundle-generation step
in the primary Go workflow.

For the traversal model and query patterns themselves, see
[Querying](/database/querying) and the [Querying Guide](/database/querying-guide/overview).

## Prerequisites

* Go 1.22 or later.
* Optional: the [Helix CLI](/cli/getting-started) for local development and ad-hoc query testing.

## Create a project

```bash theme={"languages":{"custom":["languages/helixql.json"]}}
mkdir helix-go-app
cd helix-go-app
go mod init example.com/helix-go-app
```

## Add the dependency

```bash theme={"languages":{"custom":["languages/helixql.json"]}}
go get github.com/helixdb/helix-db/sdks/go
```

Import the SDK as `helix`:

```go theme={"languages":{"custom":["languages/helixql.json"]}}
import helix "github.com/helixdb/helix-db/sdks/go"
```

## Write query functions

Go queries are normal functions. Use `helix.ReadQuery("name")` or
`helix.WriteQuery("name")` to set the dynamic request's `query_name`, then declare
runtime parameters inline with methods such as `q.ParamString`, `q.ParamI64`, and
`q.ParamDateTime`.

```go theme={"languages":{"custom":["languages/helixql.json"]}}
package users

import helix "github.com/helixdb/helix-db/sdks/go"

type UserRow struct {
	ID       int64  `json:"$id"`
	Name     string `json:"name"`
	TenantID string `json:"tenantId"`
}

type FindUsersResponse struct {
	Users []UserRow `json:"users"`
}

func FindUsers(tenantID string, limit int64) helix.Request {
	q := helix.ReadQuery("find_users")

	tenant := q.ParamString("tenant_id", tenantID)
	maxRows := q.ParamI64("limit", limit)

	return q.
		VarAs("users",
			helix.G().
				NWithLabel("User").
				Where(helix.PredEq("tenantId", tenant)).
				Limit(maxRows).
				ValueMap("$id", "name", "tenantId"),
		).
		Returning("users")
}
```

Parameter refs can be passed directly into predicates, stream bounds, and mutation
property inputs. The SDK inserts both `parameters` and `parameter_types` into the
dynamic envelope.

## Parameterize request-specific values

Direct Go values are serialized as literals in the query AST. This is useful for
true constants, but it is not a runtime parameter:

```go theme={"languages":{"custom":["languages/helixql.json"]}}
helix.G().NWhere(helix.SourceEq("id", "foo")) // embeds "foo" in the AST
```

For values that change per request, declare a `q.Param*` value and pass the returned
`ParamRef`. That keeps the query shape stable and lets the server reuse cached work
across requests:

```go theme={"languages":{"custom":["languages/helixql.json"]}}
q := helix.ReadQuery("user_by_id")
id := q.ParamString("id", userID)

return q.
	VarAs("user", helix.G().NWhere(helix.SourceEq("id", id)).ValueMap("$id", "name")).
	Returning("user")
```

The same rule applies to traversal predicates such as `helix.PredEq`, bounds such
as `Limit`, mutation properties, and search inputs: pass a `ParamRef` for
request-specific values, not a direct literal.

## Return explicit variables

The names passed to `Returning(...)` define the top-level response object keys and
should match your response struct tags:

```go theme={"languages":{"custom":["languages/helixql.json"]}}
type FindUsersResponse struct {
	Users []UserRow `json:"users"`
}

return q.VarAs("users", traversal).Returning("users")
```

Use zero-argument `Returning()` only when the response is intentionally empty, such
as an idempotent index-creation request. The SDK serializes it as `"returns":[]`.

## Execute queries

```go theme={"languages":{"custom":["languages/helixql.json"]}}
package users

import (
	"context"

	helix "github.com/helixdb/helix-db/sdks/go"
)

func ListUsers(ctx context.Context, client *helix.Client, tenantID string, limit int64) (FindUsersResponse, error) {
	var out FindUsersResponse
	err := client.Exec(ctx, FindUsers(tenantID, limit), &out)
	return out, err
}
```

Create the client once and reuse it:

```go theme={"languages":{"custom":["languages/helixql.json"]}}
client, err := helix.NewClient("https://helix.example.com", helix.WithAPIKey("hx_secret"))
if err != nil {
	return err
}

var out FindUsersResponse
err = client.Exec(ctx, FindUsers("acme", 25), &out)
```

For local development, pass `""` or `"http://localhost:6969"` to `NewClient`.

## Write queries

```go theme={"languages":{"custom":["languages/helixql.json"]}}
type CreateUserResponse struct {
	User []UserRow `json:"user"`
}

func CreateUser(name string, tenantID string) helix.Request {
	q := helix.WriteQuery("create_user")

	nameParam := q.ParamString("name", name)
	tenant := q.ParamString("tenant_id", tenantID)

	return q.
		VarAs("user",
			helix.G().AddN("User", helix.Props{
				helix.Prop("name", nameParam),
				helix.Prop("tenantId", tenant),
			}),
		).
		Returning("user")
}
```

Use execution options when a write must hit the writer node or wait for durability:

```go theme={"languages":{"custom":["languages/helixql.json"]}}
var created CreateUserResponse
err = client.Exec(ctx, CreateUser("Alice", "acme"), &created,
	helix.WriterOnly(),
	helix.AwaitDurability(true),
)
```

## Handle conflicts in application code

`Client.Exec` does not retry HTTP `409 Conflict` responses automatically. Retry only
when your operation is safe to replay. Remote errors are returned as
`*helix.HelixError` with `StatusCode` set, and conflicts wrap `helix.ErrConflict`.
Use `helix.IsConflict(err)` or `errors.Is(err, helix.ErrConflict)` instead of
parsing the error text.

```go theme={"languages":{"custom":["languages/helixql.json"]}}
func ExecWithConflictRetry(ctx context.Context, client *helix.Client, build func() helix.Request, out any) error {
	for attempt := 0; attempt < 3; attempt++ {
		err := client.Exec(ctx, build(), out)
		if err == nil || !helix.IsConflict(err) || attempt == 2 {
			return err
		}
		time.Sleep(time.Duration(attempt+1) * 50 * time.Millisecond)
	}
	return nil
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Querying" icon="magnifying-glass" href="/database/querying">
    Dynamic query envelopes, client execution, and transactions.
  </Card>

  <Card title="Parameters & bundles" icon="gear" href="/database/querying-guide/parameters-bundles">
    Parameter serialization across TypeScript, Rust, Go, and Python.
  </Card>

  <Card title="Local Development" icon="laptop-code" href="/database/local-development">
    Run HelixDB locally while developing queries.
  </Card>

  <Card title="Go SDK source" icon="github" href="https://github.com/HelixDB/helix-db/tree/main/sdks/go">
    Go module source and README.
  </Card>
</CardGroup>
