Examples

Small, runnable Go programs that demonstrate common Pebble use cases. Copy them into a file and run with go run.

Example: a simple JSON API call with retries

Retries up to three times on transient failures, with the default exponential backoff:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/example/pebble"
)

type weather struct {
    City        string  `json:"city"`
    Temperature float64 `json:"temperature_c"`
}

func main() {
    client := pebble.New(pebble.Options{
        MaxRetries: 3,
    })

    resp, err := client.Get(context.Background(), "https://api.weather.example/v1/current?city=Amsterdam")
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    var w weather
    if err := json.NewDecoder(resp.Body).Decode(&w); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s: %.1f°C\n", w.City, w.Temperature)
}

Example: posting JSON with a custom timeout

package main

import (
    "bytes"
    "context"
    "encoding/json"
    "log"
    "time"

    "github.com/example/pebble"
)

func main() {
    client := pebble.New(pebble.Options{
        Timeout: 5 * time.Second,
    })

    body, err := json.Marshal(map[string]any{
        "event": "user.signup",
        "email": "joris@example.com",
    })
    if err != nil {
        log.Fatal(err)
    }

    resp, err := client.Post(
        context.Background(),
        "https://api.events.example/v1/track",
        bytes.NewReader(body),
        "application/json",
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
}

Example: handling specific error types

package main

import (
    "context"
    "errors"
    "log"

    "github.com/example/pebble"
    "github.com/example/pebble/pebbleerrors"
)

func main() {
    client := pebble.New(pebble.Options{MaxRetries: 3})

    _, err := client.Get(context.Background(), "https://flaky-api.example/v1/data")
    if err != nil {
        var timeoutErr *pebbleerrors.TimeoutError
        var retryErr *pebbleerrors.RetryExhaustedError
        switch {
        case errors.As(err, &timeoutErr):
            log.Printf("upstream timeout after %v", timeoutErr.Elapsed)
        case errors.As(err, &retryErr):
            log.Printf("upstream failed after %d attempts: %v", retryErr.Attempts, retryErr.LastError)
        default:
            log.Printf("unexpected error: %v", err)
        }
        return
    }
}

Example: with OpenTelemetry tracing

package main

import (
    "context"

    "github.com/example/pebble"
)

func main() {
    // Assuming you've already initialised your OpenTelemetry SDK
    // and registered a tracer provider globally.
    client := pebble.New(pebble.Options{
        EnableTracing: true,
    })

    ctx, span := tracer.Start(context.Background(), "fetch-user-profile")
    defer span.End()

    resp, _ := client.Get(ctx, "https://api.users.example/v1/me")
    defer resp.Body.Close()
}

There are a few more examples in the examples/ directory of the GitHub repository, including some that use Pebble as part of larger workflows.