← Files LaunchDarklyARCHIVED FILE

skills/projects/references/go-setup.md

11.5 KB · Oct 5, 2026 · 18:24 UTC

↓ Download file

# Go Project Setup

Implementation patterns for Go applications using the LaunchDarkly API.

## Prerequisites

```bash
go get github.com/joho/godotenv
```

## Basic Project Manager

Create a package for project operations:

```go
// pkg/launchdarkly/projects.go
package launchdarkly

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

const BaseURL = "https://app.launchdarkly.com/api/v2"

type ProjectManager struct {
    apiToken string
    client   *http.Client
}

type Project struct {
    Name         string        `json:"name"`
    Key          string        `json:"key"`
    Tags         []string      `json:"tags,omitempty"`
    Environments *Environments `json:"environments,omitempty"`
}

type Environments struct {
    Items []Environment `json:"items"`
}

type Environment struct {
    Name   string `json:"name"`
    Key    string `json:"key"`
    APIKey string `json:"apiKey"`
}

func NewProjectManager(apiToken string) *ProjectManager {
    if apiToken == "" {
        apiToken = os.Getenv("LAUNCHDARKLY_API_TOKEN")
    }
    
    return &ProjectManager{
        apiToken: apiToken,
        client:   &http.Client{},
    }
}

func (pm *ProjectManager) CreateProject(name, key string, tags []string) (*Project, error) {
    payload := map[string]interface{}{
        "name": name,
        "key":  key,
    }
    if tags != nil {
        payload["tags"] = tags
    }
    
    body, err := json.Marshal(payload)
    if err != nil {
        return nil, fmt.Errorf("marshal payload: %w", err)
    }
    
    req, err := http.NewRequest("POST", fmt.Sprintf("%s/projects", BaseURL), bytes.NewReader(body))
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }
    
    req.Header.Set("Authorization", pm.apiToken)
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("do request: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode == http.StatusConflict {
        // Project exists, fetch it
        return pm.GetProject(key)
    }
    
    if resp.StatusCode != http.StatusCreated {
        body, _ := io.ReadAll(resp.Body)
        return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, body)
    }
    
    var project Project
    if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    
    return &project, nil
}

func (pm *ProjectManager) GetProject(projectKey string) (*Project, error) {
    req, err := http.NewRequest("GET", fmt.Sprintf("%s/projects/%s?expand=environments", BaseURL, projectKey), nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }
    
    req.Header.Set("Authorization", pm.apiToken)
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("do request: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("project not found: %d", resp.StatusCode)
    }
    
    var project Project
    if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    
    return &project, nil
}

func (pm *ProjectManager) GetSDKKey(projectKey, environment string) (string, error) {
    project, err := pm.GetProject(projectKey)
    if err != nil {
        return "", err
    }
    
    if project.Environments == nil {
        return "", fmt.Errorf("no environments found")
    }
    
    for _, env := range project.Environments.Items {
        if env.Key == environment {
            return env.APIKey, nil
        }
    }
    
    return "", fmt.Errorf("environment '%s' not found", environment)
}

func (pm *ProjectManager) ListProjects() ([]Project, error) {
    req, err := http.NewRequest("GET", fmt.Sprintf("%s/projects", BaseURL), nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }
    
    req.Header.Set("Authorization", pm.apiToken)
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("do request: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("failed to list projects: %d", resp.StatusCode)
    }
    
    var result struct {
        Items []Project `json:"items"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    
    return result.Items, nil
}
```

## Usage Examples

### Create a Project

```go
package main

import (
    "fmt"
    "log"
    
    "yourmodule/pkg/launchdarkly"
)

func main() {
    pm := launchdarkly.NewProjectManager("")
    
    project, err := pm.CreateProject(
        "Customer Agent Service",
        "customer-ai",
        []string{"ai-configs", "production"},
    )
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("✓ Created project: %s (%s)\n", project.Name, project.Key)
}
```

### Get SDK Key

```go
func main() {
    pm := launchdarkly.NewProjectManager("")
    
    // Get production SDK key
    sdkKey, err := pm.GetSDKKey("customer-ai", "production")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Production SDK Key: %s\n", sdkKey)
}
```

### List Projects

```go
func main() {
    pm := launchdarkly.NewProjectManager("")
    
    projects, err := pm.ListProjects()
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("Projects:")
    for _, project := range projects {
        fmt.Printf("  - %s (%s)\n", project.Name, project.Key)
    }
}
```

## HTTP Server Integration

Integrate into an HTTP server:

```go
// cmd/server/main.go
package main

import (
    "log"
    "net/http"
    "os"
    
    "yourmodule/pkg/launchdarkly"
)

func main() {
    // Initialize LaunchDarkly project
    pm := launchdarkly.NewProjectManager(os.Getenv("LAUNCHDARKLY_API_TOKEN"))
    
    project, err := pm.CreateProject(
        "Go API Service",
        "go-api-service",
        []string{"api", "ai-configs"},
    )
    if err != nil {
        log.Fatalf("Failed to initialize LaunchDarkly: %v", err)
    }
    
    // Get SDK key
    sdkKey, err := pm.GetSDKKey("go-api-service", "production")
    if err != nil {
        log.Fatalf("Failed to get SDK key: %v", err)
    }
    
    // Store SDK key for SDK initialization
    os.Setenv("LAUNCHDARKLY_SDK_KEY", sdkKey)
    
    log.Printf("✓ LaunchDarkly project ready: %s\n", project.Key)
    
    // Start server
    http.HandleFunc("/health", healthHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}
```

## CLI Tool

Create a CLI for project management:

```go
// cmd/ldprojects/main.go
package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "strings"
    
    "yourmodule/pkg/launchdarkly"
)

func main() {
    createCmd := flag.NewFlagSet("create", flag.ExitOnError)
    createName := createCmd.String("name", "", "Project name")
    createKey := createCmd.String("key", "", "Project key")
    createTags := createCmd.String("tags", "", "Comma-separated tags")
    
    listCmd := flag.NewFlagSet("list", flag.ExitOnError)
    
    getKeyCmd := flag.NewFlagSet("get-key", flag.ExitOnError)
    getKeyProject := getKeyCmd.String("project", "", "Project key")
    getKeyEnv := getKeyCmd.String("env", "production", "Environment")
    
    if len(os.Args) < 2 {
        fmt.Println("Usage: ldprojects [create|list|get-key] [options]")
        os.Exit(1)
    }
    
    pm := launchdarkly.NewProjectManager("")
    
    switch os.Args[1] {
    case "create":
        createCmd.Parse(os.Args[2:])
        if *createName == "" || *createKey == "" {
            log.Fatal("name and key are required")
        }
        
        var tags []string
        if *createTags != "" {
            tags = strings.Split(*createTags, ",")
        }
        
        project, err := pm.CreateProject(*createName, *createKey, tags)
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf("✓ Created: %s (%s)\n", project.Name, project.Key)
        
    case "list":
        listCmd.Parse(os.Args[2:])
        
        projects, err := pm.ListProjects()
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Println("Projects:")
        for _, project := range projects {
            fmt.Printf("  - %s (%s)\n", project.Name, project.Key)
        }
        
    case "get-key":
        getKeyCmd.Parse(os.Args[2:])
        if *getKeyProject == "" {
            log.Fatal("project is required")
        }
        
        sdkKey, err := pm.GetSDKKey(*getKeyProject, *getKeyEnv)
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Println(sdkKey)
        
    default:
        fmt.Println("Unknown command:", os.Args[1])
        os.Exit(1)
    }
}
```

**Usage:**
```bash
go run cmd/ldprojects/main.go create -name "My Agent" -key my-ai -tags ai-configs,production
go run cmd/ldprojects/main.go list
go run cmd/ldprojects/main.go get-key -project my-ai -env production
```

## Error Handling

Add comprehensive error handling:

```go
type LaunchDarklyError struct {
    StatusCode int
    Message    string
}

func (e *LaunchDarklyError) Error() string {
    return fmt.Sprintf("LaunchDarkly API error (%d): %s", e.StatusCode, e.Message)
}

func (pm *ProjectManager) CreateProject(name, key string, tags []string) (*Project, error) {
    // ... request setup ...
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()
    
    switch resp.StatusCode {
    case http.StatusCreated:
        var project Project
        if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
            return nil, fmt.Errorf("decode response: %w", err)
        }
        return &project, nil
        
    case http.StatusConflict:
        return pm.GetProject(key)
        
    case http.StatusUnauthorized:
        return nil, &LaunchDarklyError{resp.StatusCode, "Invalid API token"}
        
    case http.StatusForbidden:
        return nil, &LaunchDarklyError{resp.StatusCode, "Insufficient permissions (need projects:write)"}
        
    default:
        body, _ := io.ReadAll(resp.Body)
        return nil, &LaunchDarklyError{resp.StatusCode, string(body)}
    }
}
```

## Testing

Mock the HTTP client for testing:

```go
// pkg/launchdarkly/projects_test.go
package launchdarkly

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestCreateProject(t *testing.T) {
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Method != "POST" {
            t.Errorf("Expected POST, got %s", r.Method)
        }
        
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte(`{"name":"Test","key":"test","tags":[]}`))
    }))
    defer server.Close()
    
    pm := &ProjectManager{
        apiToken: "test-token",
        client:   server.Client(),
    }
    
    // Override BaseURL for test
    oldBaseURL := BaseURL
    BaseURL = server.URL
    defer func() { BaseURL = oldBaseURL }()
    
    project, err := pm.CreateProject("Test", "test", nil)
    if err != nil {
        t.Fatalf("CreateProject failed: %v", err)
    }
    
    if project.Key != "test" {
        t.Errorf("Expected key 'test', got '%s'", project.Key)
    }
}
```

## Next Steps

- [Save SDK keys to .env](env-config.md)
- [Set up project cloning](project-cloning.md)
- [Build automation tools](admin-tooling.md)

SHA-256: b60f068cb46333baa76695fc79ce49f3cdfee82f2c09625be6f6a77ad17610f9