Skip to content

About

Go client library SDK for Ably realtime messaging service

Topics

Resources

Contributing

Stars

95 stars

Watchers

25 watching

Forks

Repository files navigation

Ably Pub/Sub Go Header

Go Reference: server Go Reference: device License

Ably Pub/Sub Go SDK

The Go SDK for Ably Pub/Sub: publish and subscribe to channels, and use message history and presence, from servers and from applications on end-user devices.

Overview

The SDK has two entry points, one for each side of the connection. You choose the entry point by where your code runs, and create your client with its constructor.

Package Use it for Entry point
github.com/ably/ably-pubsub-go/v2/server/pubsub Servers and other trusted backend environments NewHTTPClient, NewRealtimeClient
github.com/ably/ably-pubsub-go/v2/device/pubsub Applications on end-user devices NewClient

Requirements

Go 1.18 or later.

Installation

Use exactly one of server/pubsub or device/pubsub in a program.

Server

go get github.com/ably/ably-pubsub-go/v2/server/pubsub

Device

go get github.com/ably/ably-pubsub-go/v2/device/pubsub

Quick start

Server

Use an API key on a server. The samples read it from the ABLY_API_KEY environment variable; set it to an API key from your Ably dashboard. The following code connects, subscribes to a channel, publishes a message to it and waits for the message to arrive.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/ably/ably-pubsub-go/v2/server/pubsub"
)

func main() {
	ctx := context.Background()

	ablyAPIKey := os.Getenv("ABLY_API_KEY")
	client, err := pubsub.NewRealtimeClient(pubsub.WithKey(ablyAPIKey))
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// Subscribing attaches the channel. Publishing while the client is still
	// connecting queues the message until it's connected.
	channel := client.Channels.Get("test-channel")
	received := make(chan *pubsub.Message, 1)
	unsubscribe, err := channel.SubscribeAll(ctx, func(msg *pubsub.Message) {
		select {
		case received <- msg:
		default:
		}
	})
	if err != nil {
		log.Fatal(err)
	}
	defer unsubscribe()

	if err := channel.Publish(ctx, "test-event", "hello world"); err != nil {
		log.Fatal(err)
	}
	msg := <-received
	fmt.Printf("Received: %v\n", msg.Data)
}

To publish over HTTP without a realtime connection, create an HTTP client instead:

package main

import (
	"context"
	"log"
	"os"

	"github.com/ably/ably-pubsub-go/v2/server/pubsub"
)

func main() {
	ablyAPIKey := os.Getenv("ABLY_API_KEY")
	client, err := pubsub.NewHTTPClient(pubsub.WithKey(ablyAPIKey))
	if err != nil {
		log.Fatal(err)
	}
	err = client.Channels.Get("test-channel").Publish(context.Background(), "test-event", "hello world")
	if err != nil {
		log.Fatal(err)
	}
}

Important

Server clients must authenticate with an API key. To use a token instead, see Server clients and token authentication.

The following endpoint issues token requests. Devices set WithAuthURL to its URL, for example https://your-server.example.com/ably-auth:

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"

	"github.com/ably/ably-pubsub-go/v2/server/pubsub"
)

func main() {
	ablyAPIKey := os.Getenv("ABLY_API_KEY")
	client, err := pubsub.NewHTTPClient(pubsub.WithKey(ablyAPIKey))
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/ably-auth", func(w http.ResponseWriter, r *http.Request) {
		// Authenticate the caller first. Grant only the operations this user needs.
		userID := "me" // the signed-in user's ID, from your session
		tokenRequest, err := client.Auth.CreateTokenRequest(&pubsub.TokenParams{
			ClientID: userID,
			// Without a capability, the token gets all of the API key's capabilities.
			Capability: `{"test-channel":["publish","subscribe","history"]}`,
		})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(tokenRequest)
	})
	log.Fatal(http.ListenAndServe(":8080", nil))
}

See token authentication for how devices use it.

Device

Authenticate a device with tokens issued by your own server, such as the endpoint above, through WithAuthURL or WithAuthCallback. Never embed an API key in an application that runs on an end-user device.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/ably/ably-pubsub-go/v2/device/pubsub"
)

func main() {
	ctx := context.Background()

	client, err := pubsub.NewClient(
		pubsub.WithAuthURL("https://your-server.example.com/ably-auth"),
		pubsub.WithClientID("me"),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// The client connects automatically. Subscribing attaches the channel.
	channel := client.Channels.Get("test-channel")
	received := make(chan *pubsub.Message, 1)
	unsubscribe, err := channel.SubscribeAll(ctx, func(msg *pubsub.Message) {
		select {
		case received <- msg:
		default:
		}
	})
	if err != nil {
		log.Fatal(err)
	}
	defer unsubscribe()

	if err := channel.Publish(ctx, "test-event", "hello world"); err != nil {
		log.Fatal(err)
	}
	msg := <-received
	fmt.Printf("Received: %v\n", msg.Data)
}

To get tokens from your own code instead of a URL, use WithAuthCallback. It returns a token, a token request or token details (pubsub.Tokener):

client, err := pubsub.NewClient(
	pubsub.WithAuthCallback(func(ctx context.Context, params pubsub.TokenParams) (pubsub.Tokener, error) {
		return requestTokenFromYourServer(ctx, params) // your function
	}),
	pubsub.WithClientID("me"),
)

Migrating from 1.x

Version 2.0 moves to a new module path and replaces the ably package with server and device entry points.

1.x 2.0
Module github.com/ably/ably-go github.com/ably/ably-pubsub-go/v2
Import github.com/ably/ably-go/ably github.com/ably/ably-pubsub-go/v2/server/pubsub or github.com/ably/ably-pubsub-go/v2/device/pubsub
Realtime client ably.NewRealtime(...) pubsub.NewRealtimeClient(...) (server) or pubsub.NewClient(...) (device)
HTTP client ably.NewREST(...) pubsub.NewHTTPClient(...) (server only)

See UPDATING.md for all other changes.

Common configuration

Both packages export the same With... options. Pass them to the constructors.

Endpoint

WithEndpoint selects the Ably host for both HTTP requests and the realtime connection. It replaces the 1.x WithEnvironment, WithRESTHost and WithRealtimeHost options.

ablyAPIKey := os.Getenv("ABLY_API_KEY")
client, err := pubsub.NewRealtimeClient(
	pubsub.WithKey(ablyAPIKey),
	pubsub.WithEndpoint("nonprod:sandbox"),
)

Never put a port in the endpoint: localhost:8080 is not a hostname and does not resolve. Set the port with WithTLSPort instead, or with WithPort when TLS is off (WithTLS(false), which needs token authentication). See Host options are replaced by WithEndpoint for how each value resolves.

Proxy

The SDK uses Go's standard proxy settings. Set the HTTP_PROXY and HTTPS_PROXY environment variables, including the scheme, and both HTTP requests and the realtime connection go through the proxy:

export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080

If the proxy needs authentication, put the credentials in the URL, for example http://username:password@proxy.example.com:8080. See http.ProxyFromEnvironment for the details.

To set a proxy in code, pass your own http.Client with WithHTTPClient. It applies to HTTP requests only; the realtime connection still uses the environment variables.

proxyURL, err := url.Parse("http://proxy.example.com:8080")
if err != nil {
	log.Fatal(err)
}
ablyAPIKey := os.Getenv("ABLY_API_KEY")
client, err := pubsub.NewHTTPClient(
	pubsub.WithKey(ablyAPIKey),
	pubsub.WithHTTPClient(&http.Client{
		Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
	}),
)

Connection reliability then depends on the proxy, and Ably can't help with errors the proxy introduces.

Logging

Set the level with WithLogLevel: LogNone, LogError, LogWarning, LogInfo, LogVerbose or LogDebug (the most verbose). The default is LogWarning. Logs go to standard error unless you pass your own Logger with WithLogHandler:

type myLogger struct{}

func (myLogger) Printf(level pubsub.LogLevel, format string, v ...interface{}) {
	log.Printf(format, v...)
}

ablyAPIKey := os.Getenv("ABLY_API_KEY")
client, err := pubsub.NewRealtimeClient(
	pubsub.WithKey(ablyAPIKey),
	pubsub.WithLogLevel(pubsub.LogDebug),
	pubsub.WithLogHandler(myLogger{}),
)

Known limitations

Further reading

Contributing

Read CONTRIBUTING.md before contributing.

Support, feedback and troubleshooting

For help, visit Ably's support page. Report bugs and compatibility problems on GitHub Issues.

License

Licensed under the Apache License, Version 2.0. See LICENSE.

About

Go client library SDK for Ably realtime messaging service

Topics

Resources

Contributing

Stars

95 stars

Watchers

25 watching

Forks

Releases

Packages

Used by

Contributors

Languages