Go SDK
On this page
Use the Go SDK from trusted server-side applications. For application writes,
start with Table.AppendStream: it accepts typed Go rows and owns bounded
encoding, batching, backpressure, and request concurrency.
Before you start
Use Go 1.24 or later. Follow Connect an application, then set the endpoint and API key used by the examples:
export SCOPEDB_ENDPOINT="https://<endpoint>"
export SCOPEDB_API_KEY="<api-key>"Keep the API key in a trusted server-side process. Do not put it in browser, mobile, or client-visible configuration.
Install
Install the maintained Go SDK module from your application module, then commit
the selected version in go.mod and go.sum:
go get github.com/scopedb/goscopedb@latestCreate a client
endpoint := os.Getenv("SCOPEDB_ENDPOINT")
apiKey := os.Getenv("SCOPEDB_API_KEY")
if endpoint == "" || apiKey == "" {
return errors.New("SCOPEDB_ENDPOINT and SCOPEDB_API_KEY are required")
}
client, err := scopedb.NewClient(scopedb.Config{
Endpoint: endpoint,
APIKey: apiKey,
})
if err != nil {
return err
}
defer client.Close()Create one client and reuse it. NewClient validates the endpoint. Set
Config.HTTPClient when the application needs to own HTTP timeouts, proxies,
TLS, or connection pooling; the SDK never closes a caller-provided client.
Query data
Query is the short path for submitting a ScopeQL statement and waiting for
its result:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
result, err := client.Query(ctx, `
FROM events
WHERE service = 'checkout'
ORDER BY time DESC
LIMIT 10
`)
if err != nil {
return err
}
rows, err := result.ToObjects()
if err != nil {
return err
}
fmt.Println(rows)SDK documentation covers client behavior, not the ScopeQL language. Use the quickstart, Query data, and ScopeQL reference for language syntax.
Work with results
Choose the representation needed by the application:
RawRowsreturns unconverted wire cell strings and nulls.ToValuesreturns positional rows with typed Go values.ToObjectskeys each typed row by column name.Firstreturns an optional first keyed row.
ToValues, ToObjects, and First convert non-null cells according to the
result metadata:
| ScopeDB result type | Go value | Notes |
|---|---|---|
int | int64 | |
uint | uint64 | |
float | float64 | Includes NaN and infinities. |
binary | []byte | Decoded bytes. |
string | string | |
boolean | bool | |
timestamp | time.Time | Preserves nanosecond precision. |
interval | time.Duration | Fixed-duration intervals only. |
array | string | JSON text; decode with encoding/json when needed. |
object | string | JSON text; decode with encoding/json when needed. |
any | string | Raw text; the SDK does not infer its dynamic Go type. |
null | nil | A null cell is nil regardless of the declared type. |
The null result type can appear in query metadata, such as for a literal
NULL; it is not a table column type. See the
data types reference for ScopeDB type semantics.
Conversions return an error for invalid cells, numeric overflow, duplicate
column names in an object conversion, or an unrecognized data type. Structured
array and object values remain JSON text; decoding them does not recursively
apply SDK conversions to nested values.
Control statement execution
Use Submit when the application needs the statement ID, a local status
snapshot, one explicit remote status request, or a separate wait:
handle, err := client.Statement("SELECT 1 AS ready").Submit(ctx)
if err != nil {
return err
}
fmt.Println("statement ID:", handle.ID())
if cached := handle.LastStatus(); cached != nil {
fmt.Println("local status:", *cached) // No network request.
}
latest, err := handle.Status(ctx) // At most one remote status request.
if err != nil {
return err
}
fmt.Println("latest status:", latest)
result, err := handle.Wait(ctx)
if err != nil {
return err
}Once a terminal status is cached, Status returns it without another request.
Persist handle.ID() and use client.StatementHandle(id) to resume the
lifecycle in another process. Cancel requests server-side cancellation and
returns the statement ID, creation time, status, and message.
Statement.ID and Statement.ExecTimeout are the only optional statement
settings. Omit ID to let ScopeDB generate one. Cancelling the Go context stops
the current client request or wait; it does not itself cancel the remote
statement.
Browse the catalog
Use iterators for normal discovery. They request REST catalog pages lazily and keep continuation tokens opaque:
for table, err := range client.IterateTables(
ctx,
"scopedb",
"public",
scopedb.CatalogListOptions{PageSize: 100},
) {
if err != nil {
return err
}
fmt.Println(table.Name)
}ListDatabases, ListSchemas, and ListTables expose one page when the
application owns pagination. FetchDatabase, FetchSchema, and FetchTable
return complete resources.
The table helper defaults to database scopedb and schema public:
table := client.Table("events")
description, err := table.Describe(ctx)
if err != nil {
return err
}
fmt.Println(description.Columns)Set table.Database and table.Schema explicitly when the destination comes
from application configuration.
Stream typed rows to a table
Use AppendStream to write rows to an existing table. Send each row with Send,
then call Shutdown when you have finished. The SDK groups rows into batches
automatically.
Rows are encoded with encoding/json, so standard JSON tags and omitempty
work as usual. Use struct tags to choose the JSON field names.
type Event struct {
ID int `json:"id"`
Name string `json:"name"`
OccurredAt time.Time `json:"occurred_at"`
Attributes map[string]string `json:"attributes,omitempty"`
}
table := client.Table("events")
stream, err := table.AppendStream(scopedb.AppendStreamOptions{})
if err != nil {
return err
}
for _, event := range []Event{
{ID: 1, Name: "checkout.started", OccurredAt: time.Now().UTC()},
{ID: 2, Name: "checkout.completed", OccurredAt: time.Now().UTC()},
} {
if err := stream.Send(ctx, event); err != nil {
_, _ = stream.Shutdown(ctx)
return err
}
}
report, err := stream.Shutdown(ctx)
if err != nil {
return err
}
fmt.Printf("wrote %d rows\n", report.CommittedRows)Send adds a row to the SDK's buffer. Shutdown sends the remaining rows,
waits for the writes to finish, and closes the stream. Check its returned error
before using the row count.
Use Flush when you want to wait for pending writes and then keep using the
same stream. See the Go SDK examples
for bulk writes and logging.
Send one raw NDJSON request
Use AppendNDJSON if your data is already in NDJSON format: one JSON object
per line.
ndjson := []byte("{\"id\":1,\"name\":\"first\"}\n{\"id\":2,\"name\":\"second\"}\n")
result, err := table.AppendNDJSON(ctx, ndjson)
if err != nil {
return err
}
fmt.Println("committed rows:", result.NumRowsInserted)One request is limited to 8 MiB of uncompressed NDJSON and 200,000 rows.
Use AppendStream to have the SDK encode and batch rows for you.
Handle structured errors
Server messages pass through unchanged. Use errors.As for operation metadata
without parsing the message:
var scopeErr *scopedb.Error
if errors.As(err, &scopeErr) {
fmt.Printf("kind=%s status=%d request_id=%s retryable=%t\n",
scopeErr.Kind,
scopeErr.HTTPStatus,
scopeErr.RequestID,
scopeErr.Retryable,
)
}StatementDetails preserves a failed statement's structured code, message,
and code-specific JSON details. AppendDetails preserves whether a table
append was rejected or has an unknown commit outcome, plus any structured
row errors.
Advanced: transform before writing
Use Client.IngestStream only when source JSON specifically needs a server-side
ScopeQL transformation before it can match the destination table. For normal
typed events, shape the row in the producer and use Table.AppendStream.
This advanced path is sequential and fail-fast. An ingest error can follow a remote commit, so reconcile the failing batch before replaying it. See Ingest data for the transform-oriented HTTP workflow.
Next steps
- Learn the language with the ScopeQL quickstart.
- Build application queries with Query data.
- Use the ScopeQL reference for exact language syntax.
- Review statement states and HTTP errors in the HTTP API.
- Browse the Go SDK examples for statement, catalog, direct append, streaming write, bulk, and telemetry workflows.