> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/trailbaseio/trailbase/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Official Go client for TrailBase

The official Go SDK for TrailBase provides a type-safe client for accessing your TrailBase backend from Go applications.

## Installation

Install using `go get`:

```bash theme={null}
go get github.com/trailbaseio/trailbase/client/go/trailbase
```

## Requirements

* Go 1.24.4+

## Initialization

### Basic Client

```go theme={null}
package main

import (
    "github.com/trailbaseio/trailbase/client/go/trailbase"
)

func main() {
    client, err := trailbase.NewClient("https://your-server.trailbase.io")
    if err != nil {
        panic(err)
    }
    defer client.Close()
}
```

### Client with Tokens

```go theme={null}
import "github.com/trailbaseio/trailbase/client/go/trailbase"

refreshToken := "your-refresh-token"
tokens := &trailbase.Tokens{
    AuthToken:    "your-auth-token",
    RefreshToken: &refreshToken,
    CsrfToken:    nil,
}

client, err := trailbase.NewClientWithTokens(
    "https://your-server.trailbase.io",
    tokens,
)
if err != nil {
    panic(err)
}
defer client.Close()
```

## Authentication

### Login

```go theme={null}
tokens, err := client.Login("user@example.com", "password")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Auth token: %s\n", tokens.AuthToken)

user := client.User()
if user != nil {
    fmt.Printf("Logged in as: %s\n", user.Email)
}
```

### Logout

```go theme={null}
err := client.Logout()
if err != nil {
    log.Fatal(err)
}
```

### Current User

```go theme={null}
user := client.User()
if user != nil {
    fmt.Printf("User ID: %s\n", user.Sub)
    fmt.Printf("Email: %s\n", user.Email)
}
```

### Access Tokens

```go theme={null}
tokens := client.Tokens()
if tokens != nil {
    // Persist tokens for later use
    data, _ := json.Marshal(tokens)
    os.WriteFile("tokens.json", data, 0600)
}
```

### Refresh Token

```go theme={null}
err := client.Refresh()
if err != nil {
    log.Fatal(err)
}
```

## Record API

### Define Your Record Types

```go theme={null}
type Post struct {
    ID        string `json:"id"`
    Title     string `json:"title"`
    Content   string `json:"content"`
    AuthorID  string `json:"author_id"`
    CreatedAt int64  `json:"created_at"`
}

type NewPost struct {
    Title   string `json:"title"`
    Content string `json:"content"`
}
```

### List Records

```go theme={null}
import "github.com/trailbaseio/trailbase/client/go/trailbase"

posts := trailbase.NewRecordApi(client, "posts")

response, err := posts.List(
    &trailbase.ListOptions{
        Limit:  10,
        Offset: 0,
        Order:  []string{"-created_at"},
        Count:  true,
    },
)
if err != nil {
    log.Fatal(err)
}

var postList []Post
err = json.Unmarshal(response.Records, &postList)

fmt.Printf("Records: %d\n", len(postList))
fmt.Printf("Total count: %v\n", response.TotalCount)
fmt.Printf("Next cursor: %v\n", response.Cursor)
```

### Read a Record

```go theme={null}
// String ID
data, err := posts.Read("post-id", nil)
if err != nil {
    log.Fatal(err)
}

var post Post
err = json.Unmarshal(data, &post)

fmt.Printf("Title: %s\n", post.Title)
```

### Read with Expansion

```go theme={null}
data, err := posts.Read("post-id", &trailbase.ReadOptions{
    Expand: []string{"author"},
})
if err != nil {
    log.Fatal(err)
}

var post Post
json.Unmarshal(data, &post)
```

### Create a Record

```go theme={null}
newPost := NewPost{
    Title:   "Hello World",
    Content: "My first post from Go",
}

postID, err := posts.Create(&newPost)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Created post with ID: %s\n", postID)
```

### Create Multiple Records

```go theme={null}
newPosts := []NewPost{
    {Title: "Post 1", Content: "Content 1"},
    {Title: "Post 2", Content: "Content 2"},
}

ids, err := posts.CreateBulk(&newPosts)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Created %d posts\n", len(ids))
```

### Update a Record

```go theme={null}
update := map[string]interface{}{
    "title": "Updated Title",
}

err := posts.Update("post-id", &update)
if err != nil {
    log.Fatal(err)
}
```

### Delete a Record

```go theme={null}
err := posts.Delete("post-id")
if err != nil {
    log.Fatal(err)
}
```

## Filtering

```go theme={null}
import "github.com/trailbaseio/trailbase/client/go/trailbase"

// Simple equality filter
filters := []trailbase.FilterOrComposite{
    trailbase.Filter{
        Column: "author_id",
        Value:  userID,
    },
}

response, err := posts.List(&trailbase.ListOptions{
    Filters: filters,
})

// With comparison operators
weekAgo := time.Now().AddDate(0, 0, -7).Unix()
filters = []trailbase.FilterOrComposite{
    trailbase.Filter{
        Column: "created_at",
        Op:     trailbase.CompareOpGreaterThan,
        Value:  fmt.Sprintf("%d", weekAgo),
    },
}

// LIKE operator for text search
filters = []trailbase.FilterOrComposite{
    trailbase.Filter{
        Column: "title",
        Op:     trailbase.CompareOpLike,
        Value:  "%search%",
    },
}

// AND composite filter
filters = []trailbase.FilterOrComposite{
    trailbase.And{
        Filters: []trailbase.FilterOrComposite{
            trailbase.Filter{Column: "status", Value: "published"},
            trailbase.Filter{Column: "author_id", Value: userID},
        },
    },
}

// OR composite filter
filters = []trailbase.FilterOrComposite{
    trailbase.Or{
        Filters: []trailbase.FilterOrComposite{
            trailbase.Filter{Column: "category", Value: "tech"},
            trailbase.Filter{Column: "category", Value: "science"},
        },
    },
}
```

### Available Comparison Operators

```go theme={null}
const (
    CompareOpEqual              CompareOp = "$eq"
    CompareOpNotEqual           CompareOp = "$ne"
    CompareOpLessThan           CompareOp = "$lt"
    CompareOpLessThanEqual      CompareOp = "$lte"
    CompareOpGreaterThan        CompareOp = "$gt"
    CompareOpGreaterThanEqual   CompareOp = "$gte"
    CompareOpLike               CompareOp = "$like"
    CompareOpRegexp             CompareOp = "$re"
    CompareOpStWithin           CompareOp = "@within"      // Geospatial
    CompareOpStIntersects       CompareOp = "@intersects"  // Geospatial
    CompareOpStContains         CompareOp = "@contains"    // Geospatial
)
```

## Error Handling

```go theme={null}
data, err := posts.Read("post-id", nil)
if err != nil {
    if httpErr, ok := err.(*trailbase.HttpError); ok {
        fmt.Printf("HTTP %d: %s\n", httpErr.StatusCode, httpErr.Message)
    } else {
        fmt.Printf("Error: %v\n", err)
    }
    return
}

var post Post
json.Unmarshal(data, &post)
```

## Type Definitions

### User

```go theme={null}
type User struct {
    Sub   string
    Email string
}
```

### Tokens

```go theme={null}
type Tokens struct {
    AuthToken    string  `json:"auth_token"`
    RefreshToken *string `json:"refresh_token,omitempty"`
    CsrfToken    *string `json:"csrf_token,omitempty"`
}
```

### ListResponse

```go theme={null}
type ListResponse struct {
    Cursor     *string         `json:"cursor,omitempty"`
    TotalCount *int            `json:"total_count,omitempty"`
    Records    json.RawMessage `json:"records"`
}
```

### ListOptions

```go theme={null}
type ListOptions struct {
    Pagination *Pagination
    Order      []string
    Filters    []FilterOrComposite
    Count      bool
    Expand     []string
}
```

### Pagination

```go theme={null}
type Pagination struct {
    Cursor *string
    Limit  *int
    Offset *int
}
```

## Best Practices

<Note>
  Always check errors returned by SDK methods. Go's explicit error handling ensures you don't miss failure cases.
</Note>

<Warning>
  Store tokens securely and never commit them to version control. Consider using environment variables or secure credential storage.
</Warning>

<Info>
  The client automatically refreshes auth tokens before they expire through concurrent-safe token management.
</Info>

## Example Application

```go theme={null}
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "os"
    
    "github.com/trailbaseio/trailbase/client/go/trailbase"
)

type Post struct {
    ID        string `json:"id"`
    Title     string `json:"title"`
    Content   string `json:"content"`
    Published bool   `json:"published"`
}

func main() {
    // Initialize client
    url := os.Getenv("TRAILBASE_URL")
    if url == "" {
        url = "http://localhost:4000"
    }
    
    client, err := trailbase.NewClient(url)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()
    
    // Login
    _, err = client.Login(
        os.Getenv("TRAILBASE_EMAIL"),
        os.Getenv("TRAILBASE_PASSWORD"),
    )
    if err != nil {
        log.Fatal("Login failed:", err)
    }
    
    if user := client.User(); user != nil {
        fmt.Printf("Logged in as: %s\n", user.Email)
    }
    
    // List posts
    posts := trailbase.NewRecordApi(client, "posts")
    
    response, err := posts.List(&trailbase.ListOptions{
        Order: []string{"-created_at"},
        Pagination: &trailbase.Pagination{
            Limit: intPtr(10),
        },
        Filters: []trailbase.FilterOrComposite{
            trailbase.Filter{
                Column: "published",
                Value:  "true",
            },
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    
    var postList []Post
    json.Unmarshal(response.Records, &postList)
    
    fmt.Printf("\nFound %d posts:\n", len(postList))
    for _, post := range postList {
        fmt.Printf("- %s\n", post.Title)
    }
    
    // Create a new post
    newPost := map[string]interface{}{
        "title":     "Hello from Go",
        "content":   "This post was created using the TrailBase Go SDK",
        "published": true,
    }
    
    newPostID, err := posts.Create(&newPost)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("\nCreated new post with ID: %s\n", newPostID)
    
    // Read the post
    data, err := posts.Read(newPostID, nil)
    if err != nil {
        log.Fatal(err)
    }
    
    var post Post
    json.Unmarshal(data, &post)
    fmt.Printf("Post title: %s\n", post.Title)
    
    // Logout
    err = client.Logout()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("\nLogged out")
}

func intPtr(i int) *int {
    return &i
}
```
