Query builder
This feature is provided by the contrib/surrealql package, which is outside of the backward compatibility guarantees of the core SDK. Its API may change without following semantic versioning.
The surrealql query builder lets you construct SurrealQL queries programmatically using a fluent Go API. It automatically binds values as parameters to prevent injection, and produces a query string and variables map that you can pass directly to surrealdb.Query.
Installing the package
The query builder is part of the SDK module. Import it alongside the main package:
import "github.com/surrealdb/surrealdb.go/contrib/surrealql"
Building and executing queries
Every query builder type has a .Build() method that returns a SurrealQL string and a map[string]any of parameters. Pass these directly to surrealdb.Query:
q := surrealql.Select("users").Where("age > ?", 18).Limit(10)
sql, vars := q.Build()
results, err := surrealdb.Query[[]User](ctx, db, sql, vars)
Selecting records
Use Select to start a SELECT query. Chain .Fields(), .Where(), .OrderBy(), .Limit(), and other methods to refine it.
surrealql.Select("users")
surrealql.Select("users").Fields("name", "email").Where("active = ?", true)
surrealql.Select("users").
Field(surrealql.Expr("count()").As("total")).
GroupBy("department")
SelectOnly produces a SELECT ... ONLY query that returns a single record instead of an array. You can pass RecordID and Table values directly as targets:
surrealql.SelectOnly(models.NewRecordID("users", "tobie")).Fields("name")
Filtering with Where
The .Where() method accepts a condition string with ? placeholders. Values are bound as parameters automatically.
surrealql.Select("products").
Where("price < ?", 100).
Where("category = ?", "electronics")
Ordering and pagination
surrealql.Select("users").
OrderByDesc("created_at").
Limit(20).
Start(40)
Creating records
Use Create to build a CREATE query. Set fields with .Set() or provide the entire content with .Content().
surrealql.Create("users").
Set("name", "Alice").
Set("age", 30)
surrealql.Create("users").Content(map[string]any{
"name": "Alice",
"age": 30,
})
CreateOnly produces a CREATE ... ONLY query:
surrealql.CreateOnly("users").Set("name", "Alice").ReturnNone()
Updating records
Use Update to build an UPDATE query with .Set() and .Where():
surrealql.Update("users").
Set("active", false).
Where("last_login < ?", "2025-01-01")
Compound operations are supported in .Set():
surrealql.Update("products").Set("stock -= ?", 1).Where("id = ?", "products:apple")
Upserting records
Use Upsert to build an UPSERT query. This creates a record if it does not exist, or updates it if it does. After calling Upsert, choose one of the data modes: .Set(), .Content(), .Merge(), .Patch(), or .Replace().
surrealql.Upsert("users:tobie").Set("name", "Tobie").Set("active", true)
surrealql.Upsert("users:tobie").Content(map[string]any{
"name": "Tobie",
"active": true,
})
surrealql.Upsert("users:tobie").Merge(map[string]any{"active": false})
Upsert queries also support .Where(), .Timeout(), .Parallel(), and .Explain().
Deleting records
Use Delete to build a DELETE query:
surrealql.Delete("sessions").Where("expired_at < ?", "2025-01-01")
Inserting records
Use Insert to build an INSERT query. This is useful for bulk inserts and inserting from subqueries.
surrealql.Insert("users").Value(map[string]any{
"name": "Alice",
"age": 30,
})
surrealql.Insert("users").
Fields("name", "age").
Values("Alice", 30).
Values("Bob", 25)
surrealql.Insert("users").Relation().Value(map[string]any{
"in": "users:tobie",
"out": "posts:first",
})
Handle conflicts with ON DUPLICATE KEY UPDATE:
surrealql.Insert("products").
Fields("id", "stock").
Values("products:apple", 10).
OnDuplicateKeyUpdateRaw("stock += $input.stock")
Creating relations
Use Relate to build a RELATE query between two records:
surrealql.Relate(
models.NewRecordID("users", "tobie"),
"wrote",
models.NewRecordID("posts", "first"),
).Set("created_at", "2026-01-01")
Using expressions
The Expr function creates parameterized expressions with ? placeholders. Use it for function calls, graph traversals, or any expression that needs value binding.
surrealql.Select("users").
Field(surrealql.Expr("math::mean([?, ?, ?])", 1, 2, 3).As("avg"))
surrealql.Select(surrealql.Expr("?->knows->users", models.NewRecordID("users", "tobie")))
Return clauses
All mutation queries support return clauses:
surrealql.Create("users").Set("name", "Alice").ReturnNone()
surrealql.Update("users").Set("active", true).Return("AFTER")
surrealql.Delete("sessions").ReturnBefore()
Building text-based transactions
The query builder can compose text-based transactions with Begin:
q := surrealql.Begin().
Then(surrealql.Create("users").Set("name", "Alice")).
Then(surrealql.Create("users").Set("name", "Bob"))
sql, vars := q.Build()
Text-based transactions built with the query builder are different from interactive transactions. Text-based transactions execute all statements atomically in a single RPC call. Interactive transactions allow inspecting results between statements.
Learn more