Skip to content

Go client

A Go client with the same behaviour as the Node client. It uses only the standard library and needs Go 1.22 or newer.

Terminal window
go get github.com/degoke/browserbay/clients/go
import browserbay "github.com/degoke/browserbay/clients/go"
box, err := browserbay.New(browserbay.Options{
URL: "https://browsers.example.com",
APIKey: os.Getenv("BROWSERBAY_API_KEY"),
})
if err != nil {
log.Fatal(err)
}
slot, err := box.Acquire(ctx, browserbay.AcquireOptions{
RunID: "task-42",
MaxLifetime: 30 * time.Minute,
})
if errors.Is(err, browserbay.ErrNoCapacity) {
log.Fatal("every browser is busy, try again later")
} else if err != nil {
log.Fatal(err)
}
defer slot.Release(ctx) // idempotent
fmt.Println(slot.CDPURL, slot.Headers())

Give the slot’s address and headers to any DevTools client. With playwright-go:

browser, err := pw.Chromium.ConnectOverCDP(slot.CDPURL, playwright.BrowserTypeConnectOverCDPOptions{
Headers: map[string]string{browserbay.TokenHeader: slot.Token},
})
page, err := browser.Contexts()[0].NewPage() // the slot's own context

With a raw WebSocket library such as gorilla/websocket, slot.Headers() returns an http.Header:

conn, _, err := websocket.DefaultDialer.DialContext(ctx, slot.CDPURL, slot.Headers())

The live view guide shows what to send over such a connection.

CallDoes
box.Acquire(ctx, opts)Takes a browser. ErrNoCapacity when the box stays full for the whole Wait (default 30 s).
box.Slot(stored)A slot from what you saved earlier. Makes no request.
box.Release(ctx, slotID)Releases a slot you only know the id of. ReleaseAt(ctx, boxURL, slotID) for one on another box.
box.List(ctx)Slots (this namespace’s, if you set one), with their run ids.
box.Nodes(ctx)Nodes and their load.
box.Reap(ctx, opts)Releases slots whose run IsLive says is gone. Returns how many.
slot.IsAlive(ctx)Never fails. A box that cannot be asked counts as “not alive”.
slot.Release(ctx)Closes the browser. Idempotent.
json.Marshal(slot)What to save to come back later. It holds the slot token: store it like a secret.

Options also takes Namespace, AllowOtherBoxes (a list of addresses a stored slot may name), AllowAnyBox, HTTPClient, Logger, Now and RetryDelay.

  • Zero means the default. To ask for the opposite use browserbay.NoWait (do not wait for a free slot), browserbay.NoGrace (reap slots of any age) and browserbay.NoRetryDelay.
  • Every call takes a context.Context. Cancel it to stop waiting.
  • Redirects. The default HTTP client refuses redirects to another host, because Go forwards custom headers such as the slot token on them. A client you supply is used as it is.
  • Errors. errors.Is(err, browserbay.ErrNoCapacity) for a full box, ErrQuotaExceeded when the API key’s quota is used up (answered at once, without waiting), ErrForbidden when the key is confined to another namespace (make the client with the matching Namespace), ErrInvalidArgument for options refused before anything is sent, ErrUntrustedBox for a stored slot naming a box you have not allowed, and errors.As(err, &apiErr) with var apiErr *browserbay.Error for the HTTP status of anything else.

A saved slot is the same JSON as the Node and Python clients write, so any of the three can revive the others’ slots. See Hand off and clean up.

The full reference is in the module’s README.