Skip to main content

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.WithInstance(os.Getenv("GLEAN_INSTANCE")),
)

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

client := glean.New(
glean.WithAPIToken("your-user-token"),
glean.WithInstance("your-company"),
)

Global Tokens with ActAs

ctx := context.Background()

// Add ActAs header to context
ctx = context.WithValue(ctx, "X-Glean-ActAs", "user@company.com")

response, err := client.Client.Chat.Create(ctx, &glean.ChatRequest{
Messages: []glean.ChatMessage{{
Fragments: []glean.ChatMessageFragment{{
Text: "Hello",
}},
}},
})

OAuth Authentication

OAuth allows you to use access tokens from your identity provider (Google, Azure, Okta, etc.) instead of Glean-issued tokens.

Prerequisites

OAuth requests require these headers:

HeaderValue
AuthorizationBearer <oauth_access_token>
X-Glean-Auth-TypeOAUTH

Example: Authorization Code Flow

This example uses golang.org/x/oauth2:

package main

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

"golang.org/x/oauth2"
glean "github.com/gleanwork/api-client-go"
)

var oauthConfig = &oauth2.Config{
ClientID: os.Getenv("OAUTH_CLIENT_ID"),
ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"),
RedirectURL: "http://localhost:8080/callback",
Scopes: []string{"openid", "email"},
Endpoint: oauth2.Endpoint{
AuthURL: os.Getenv("OAUTH_AUTH_URL"),
TokenURL: os.Getenv("OAUTH_TOKEN_URL"),
},
}

// oauthTransport adds OAuth headers to all requests
type oauthTransport struct {
token string
transport http.RoundTripper
}

func (t *oauthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("Authorization", "Bearer "+t.token)
req.Header.Set("X-Glean-Auth-Type", "OAUTH")
return t.transport.RoundTrip(req)
}

func main() {
http.HandleFunc("/login", handleLogin)
http.HandleFunc("/callback", handleCallback)
http.ListenAndServe(":8080", nil)
}

func handleLogin(w http.ResponseWriter, r *http.Request) {
url := oauthConfig.AuthCodeURL("state")
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}

func handleCallback(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
token, err := oauthConfig.Exchange(context.Background(), code)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// Create HTTP client with OAuth headers
httpClient := &http.Client{
Transport: &oauthTransport{
token: token.AccessToken,
transport: http.DefaultTransport,
},
}

// Create Glean client with custom HTTP client
client := glean.New(
glean.WithInstance(os.Getenv("GLEAN_INSTANCE")),
glean.WithClient(httpClient),
)

results, err := client.Client.Search.Query(r.Context(), "quarterly reports", nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

json.NewEncoder(w).Encode(results)
}
tip

Access tokens typically expire after ~1 hour. For production use, use oauth2.Config.TokenSource for automatic refresh.

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)
}

Additional Resources