Go API Client
Glean's Go API client provides idiomatic Go interfaces for integrating enterprise search and AI capabilities into your Go applications.
api-client-go
Official Go client for Glean's Client API
Installation
go get github.com/gleanwork/api-client-go
Quick Start
package main
import (
"context"
"fmt"
"os"
glean "github.com/gleanwork/api-client-go"
)
func main() {
ctx := context.Background()
client := glean.New(
glean.WithAPIToken(os.Getenv("GLEAN_API_TOKEN")),
glean.WithServerURL(os.Getenv("GLEAN_SERVER_URL")),
)
message := glean.ChatMessageFragment{
Text: "What are our company values?",
}
res, err := client.Client.Chat.Create(ctx, &glean.ChatRequest{
Messages: []glean.ChatMessage{{
Fragments: []glean.ChatMessageFragment{message},
}},
})
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println(res.Text)
}
Core Features
Chat API
func chatExample(client *glean.Client) error {
ctx := context.Background()
response, err := client.Client.Chat.Create(ctx, &glean.ChatRequest{
Messages: []glean.ChatMessage{{
Fragments: []glean.ChatMessageFragment{{
Text: "Explain our Q4 strategy",
}},
}},
})
if err != nil {
return err
}
fmt.Println(response.Text)
return nil
}
Search API
func searchExample(client *glean.Client) error {
ctx := context.Background()
results, err := client.Client.Search.Search(ctx, &glean.SearchRequest{
Query: "quarterly business review",
PageSize: glean.Int(10),
})
if err != nil {
return err
}
for _, result := range results.Results {
fmt.Printf("Title: %s\n", result.Title)
fmt.Printf("URL: %s\n", result.URL)
}
return nil
}
Framework Integrations
Gin Web Framework
package main
import (
"net/http"
"github.com/gin-gonic/gin"
glean "github.com/gleanwork/api-client-go"
)
type ChatRequest struct {
Message string `json:"message"`
}
func setupRoutes(client *glean.Client) *gin.Engine {
r := gin.Default()
r.POST("/chat", func(c *gin.Context) {
var req ChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
response, err := client.Client.Chat.Create(c.Request.Context(), &glean.ChatRequest{
Messages: []glean.ChatMessage{{
Fragments: []glean.ChatMessageFragment{{
Text: req.Message,
}},
}},
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"response": response.Text})
})
return r
}
Echo Framework
import (
"net/http"
"github.com/labstack/echo/v4"
glean "github.com/gleanwork/api-client-go"
)
func chatHandler(client *glean.Client) echo.HandlerFunc {
return func(c echo.Context) error {
var req ChatRequest
if err := c.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
response, err := client.Client.Chat.Create(c.Request().Context(), &glean.ChatRequest{
Messages: []glean.ChatMessage{{
Fragments: []glean.ChatMessageFragment{{
Text: req.Message,
}},
}},
})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
return c.JSON(http.StatusOK, map[string]string{
"response": response.Text,
})
}
}
Concurrency Patterns
Batch Processing with Goroutines
func batchSearch(client *glean.Client, queries []string) ([]glean.SearchResponse, error) {
ctx := context.Background()
results := make([]glean.SearchResponse, len(queries))
errors := make([]error, len(queries))
var wg sync.WaitGroup
for i, query := range queries {
wg.Add(1)
go func(index int, q string) {
defer wg.Done()
result, err := client.Client.Search.Search(ctx, &glean.SearchRequest{
Query: q,
})
if err != nil {
errors[index] = err
return
}
results[index] = *result
}(i, query)
}
wg.Wait()
// Check for errors
for _, err := range errors {
if err != nil {
return nil, err
}
}
return results, nil
}
Authentication
User-Scoped Tokens
client := glean.New(
glean.WithAPIToken("your-user-token"),
glean.WithServerURL("https://your-server-id-be.glean.com"),
)
Global Tokens with ActAs
Per-request headers are passed with operations.WithSetHeaders:
import (
glean "github.com/gleanwork/api-client-go"
"github.com/gleanwork/api-client-go/models/components"
"github.com/gleanwork/api-client-go/models/operations"
)
response, err := client.Client.Chat.Create(ctx, components.ChatRequest{
Messages: []components.ChatMessage{{
Fragments: []components.ChatMessageFragment{{
Text: glean.String("Hello"),
}},
}},
}, nil, operations.WithSetHeaders(map[string]string{
"X-Glean-ActAs": "user@company.com",
}))
OAuth Access Tokens
An OAuth access token is a bearer credential, so it goes in the same WithSecurity option:
client := glean.New(
glean.WithSecurity(oauthAccessToken),
glean.WithServerURL("https://your-server-id-be.glean.com"),
)
Tokens issued by the Glean OAuth Authorization Server (including tokens obtained via Dynamic Client Registration) are detected automatically. Tokens issued by an external identity provider (Google, Okta, Azure, etc.) additionally require the X-Glean-Auth-Type: OAUTH header on each request:
import (
"github.com/gleanwork/api-client-go/models/components"
"github.com/gleanwork/api-client-go/models/operations"
)
results, err := client.Client.Search.Query(ctx, &components.SearchRequest{
Query: "quarterly reports",
}, operations.WithSetHeaders(map[string]string{
"X-Glean-Auth-Type": "OAUTH",
}))
See the OAuth authentication guide for identity-provider setup.
Complete Example: Authorization Code with PKCE
This example uses golang.org/x/oauth2. It runs the Authorization Code flow with PKCE (S256ChallengeOption / VerifierOption) and AccessTypeOffline for a refresh token, then passes the access token to the Glean client. Read AuthURL/TokenURL from the issuer's metadata document.
package main
import (
"context"
"encoding/json"
"net/http"
"os"
"golang.org/x/oauth2"
glean "github.com/gleanwork/api-client-go"
"github.com/gleanwork/api-client-go/models/components"
"github.com/gleanwork/api-client-go/models/operations"
)
var (
conf = &oauth2.Config{
ClientID: os.Getenv("OAUTH_CLIENT_ID"),
ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"), // omit for a public client
RedirectURL: "http://localhost:8080/callback",
Scopes: []string{"openid", "offline_access", "SEARCH"}, // SEARCH lets the token call /search; offline_access → refresh token
Endpoint: oauth2.Endpoint{
AuthURL: os.Getenv("OAUTH_AUTH_URL"),
TokenURL: os.Getenv("OAUTH_TOKEN_URL"),
},
}
// Demo only: generate and store the verifier per request (e.g. in session) in production.
verifier = oauth2.GenerateVerifier()
)
func main() {
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline,
oauth2.S256ChallengeOption(verifier))
http.Redirect(w, r, url, http.StatusFound)
})
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tok, err := conf.Exchange(ctx, r.URL.Query().Get("code"), oauth2.VerifierOption(verifier))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
client := glean.New(
glean.WithSecurity(tok.AccessToken),
glean.WithServerURL(os.Getenv("GLEAN_SERVER_URL")),
)
results, err := client.Client.Search.Query(ctx, &components.SearchRequest{
Query: "quarterly reports",
}, // Omit this option when the token is from the Glean Authorization Server.
operations.WithSetHeaders(map[string]string{"X-Glean-Auth-Type": "OAUTH"}))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(results)
})
http.ListenAndServe(":8080", nil)
}
Access tokens expire. Wrap the token in conf.TokenSource(ctx, tok) to refresh automatically when you requested offline_access.
Error Handling
func safeChat(client *glean.Client, message string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
response, err := client.Client.Chat.Create(ctx, &glean.ChatRequest{
Messages: []glean.ChatMessage{{
Fragments: []glean.ChatMessageFragment{{
Text: message,
}},
}},
})
if err != nil {
return "", fmt.Errorf("chat error: %w", err)
}
return response.Text, nil
}
Testing
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestChatService(t *testing.T) {
// Mock implementation
mockClient := &MockGleanClient{}
expectedResponse := &glean.ChatResponse{
Text: "Test response",
}
mockClient.On("Create", mock.Anything, mock.Anything).Return(expectedResponse, nil)
// Test your service here
assert.NotNil(t, expectedResponse)
}