-
Notifications
You must be signed in to change notification settings - Fork 0
Examples and Recipes
This page collects practical starting points for common goldsky-go tasks. Each example uses the SDK’s explicit context and error model. Before adapting any mutation, decide how your application will reconcile a timeout or duplicate attempt.
The repository also includes eight runnable, focused programs under examples/. They are useful for verifying credentials and seeing imports in a complete package.1
| Example directory | Requirement | Purpose |
|---|---|---|
01-list-pipelines |
GOLDSKY_API_KEY |
Lists one page of pipelines. |
02-paginate-subgraphs |
GOLDSKY_API_KEY |
Walks subgraph pages safely. |
03-validate-pipeline |
GOLDSKY_API_KEY |
Validates a pipeline without creation. |
04-create-pipeline |
API key and GOLDSKY_RUN_MUTATIONS=1
|
Creates a guarded test pipeline. |
05-graphql-query |
API key and subgraph identifiers | Queries a private GraphQL endpoint. |
06-edge-rpc |
Project API key and Edge API key | Calls eth_blockNumber. |
07-verify-webhook |
GOLDSKY_WEBHOOK_SECRET |
Starts a local webhook verifier. |
08-handle-errors |
API key and pipeline name | Classifies several error types. |
Run one from the repository root:
go run ./examples/01-list-pipelinesCreate the SDK client once, then pass it to components that need it. This keeps credentials and transport configuration centralized.
type PipelineReader struct {
client *goldsky.Client
}
func (r PipelineReader) Names(ctx context.Context) ([]string, error) {
page, err := r.client.Pipelines.List(ctx, goldsky.ListPipelinesOptions{PageSize: 100})
if err != nil {
return nil, err
}
names := make([]string, 0, len(page.Data))
for _, pipeline := range page.Data {
names = append(names, pipeline.Name)
}
return names, nil
}Use a parent request or job context at the boundary, and apply a deadline there. This enables cancellation to reach the underlying HTTP request.
A deployed subgraph can exist before it is healthy and synchronized. The following bounded poller inspects a version or tag and exits when it reaches the readiness condition that this application requires.
func waitForSubgraph(ctx context.Context, client *goldsky.Client, name, version string) (goldsky.Subgraph, error) {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
page, err := client.Subgraphs.GetVersion(ctx, name, version)
if err != nil {
return goldsky.Subgraph{}, err
}
for _, item := range page.Data {
if item.Status == goldsky.SubgraphStatusActive &&
item.Health == goldsky.SubgraphHealthHealthy &&
item.Synced {
return item, nil
}
}
select {
case <-ctx.Done():
return goldsky.Subgraph{}, ctx.Err()
case <-ticker.C:
}
}
}Use a caller deadline such as 10 minutes for a deployment workflow. Do not treat ACTIVE alone as proof that a subgraph has finished indexing.
A tag promotion is a simple release pattern for stable GraphQL consumers.
const (
name = "dex-analytics"
version = "1.2.0"
releaseTag = "prod"
)
bundle, err := os.Open("build.zip")
if err != nil {
return err
}
defer bundle.Close()
if _, err := client.Subgraphs.Deploy(ctx, name, version, goldsky.DeploySubgraphOptions{
Bundle: bundle, BundleFilename: "build.zip",
}); err != nil {
return err
}
if _, err := waitForSubgraph(ctx, client, name, version); err != nil {
return err
}
if _, err := client.Subgraphs.SetTag(ctx, name, releaseTag, goldsky.SetSubgraphTagRequest{
TargetVersion: version,
}); err != nil {
return err
}The bundle upload is intentionally single-attempt. If it returns a transport error, call GetVersion before deploying again. See Subgraphs.
Centralize error classification so that HTTP handlers, workers, and CLI commands have consistent behavior.
func classifyGoldskyError(err error) (code int, message string) {
if err == nil {
return http.StatusOK, "ok"
}
if p := goldsky.AsProblem(err); p != nil {
switch {
case p.IsValidation():
return http.StatusBadRequest, "Goldsky rejected the request"
case p.IsAuthentication(), p.IsPermission():
return http.StatusBadGateway, "Goldsky authorization failed"
case p.IsNotFound():
return http.StatusNotFound, "Goldsky resource not found"
case p.IsRateLimited(), p.IsServerError():
return http.StatusServiceUnavailable, "Goldsky is temporarily unavailable"
}
}
if goldsky.AsTransport(err) != nil {
return http.StatusGatewayTimeout, "Goldsky request could not complete"
}
return http.StatusInternalServerError, "unexpected Goldsky integration error"
}Do not send raw Goldsky Detail, raw response bodies, or credentials to an untrusted caller. Preserve diagnostic detail in secure internal logs.
The typed pagers provide bounded-memory iteration. Process each resource before fetching the next page.
pager := client.Pipelines.NewPipelinePager(goldsky.ListPipelinesOptions{PageSize: 200})
for {
page, err := pager.NextPage(ctx)
if err != nil {
return err
}
for _, pipeline := range page.Data {
if err := reconcilePipeline(ctx, pipeline); err != nil {
return err
}
}
if !page.HasMore() {
break
}
}This pattern is preferable to accumulating an unbounded slice in a controller or scheduled job.
response, err := client.GraphQL.QueryPrivate(ctx, projectID, "dex-analytics", "prod", goldsky.GraphQLRequest{
OperationName: "TradesAfter",
Query: `query TradesAfter($timestamp: BigInt!) {
trades(where: { timestamp_gt: $timestamp }, first: 100) {
id timestamp amount
}
}`,
Variables: map[string]any{"timestamp": "1700000000"},
})
if err != nil {
return err
}
if response.HasErrors() {
return fmt.Errorf("GraphQL error: %s", response.Errors[0].Message)
}
var payload struct {
Trades []struct {
ID string `json:"id"`
Timestamp string `json:"timestamp"`
Amount string `json:"amount"`
} `json:"trades"`
}
if err := json.Unmarshal(response.Data, &payload); err != nil {
return err
}Use the exact scalar types from the subgraph schema. The example’s BigInt is only illustrative; query the deployed subgraph’s schema to confirm the field and scalar names.
var chainID, latestBlock string
responses, err := client.RPC.Batch(ctx, 1, []goldsky.RPCBatchCall{
{Method: "eth_chainId", Result: &chainID},
{Method: "eth_blockNumber", Result: &latestBlock},
})
if err != nil {
return err
}
for _, response := range responses {
if response.Error != nil {
return response.Error
}
}
fmt.Println(chainID, latestBlock)The results remain aligned with the request order even if the server returns its batch response in another order. See Edge RPC.
func (s *Server) goldskyWebhook(w http.ResponseWriter, r *http.Request) {
if !goldsky.VerifyWebhookRequest(r, s.webhookSecret) {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
var event Event
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&event); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
if err := s.eventStore.InsertIfAbsent(r.Context(), event.ID, event); err != nil {
http.Error(w, "temporary failure", http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusNoContent)
}The durable InsertIfAbsent step is the important part. It allows a retry to be treated as a successful duplicate and stops the client from receiving an acknowledgement before the work is protected.