A production-ready Go client library for the JustStorage object storage service.
- ✅ Full API Coverage: Upload, download, list, and delete operations
- ✅ Streaming Support: Efficient streaming for large files
- ✅ Type-Safe: Strongly typed structs matching the API
- ✅ Authentication: Support for JWT Bearer tokens and API keys
- ✅ Error Handling: Comprehensive error types with helper functions
- ✅ Context Support: Full context.Context support for cancellation and timeouts
- ✅ Production Ready: Follows Go best practices and conventions
go get github.com/just-storage/go-sdkpackage main
import (
"context"
"fmt"
"io"
"os"
"github.com/just-storage/go-sdk"
)
func main() {
// Create client with Bearer token
client, err := sdk.NewClientWithBearerToken(
"http://localhost:8080",
"your-jwt-token",
)
if err != nil {
panic(err)
}
ctx := context.Background()
// Upload a file
file, _ := os.Open("model.bin")
defer file.Close()
obj, err := client.Upload(ctx, file, sdk.UploadOptions{
Namespace: "models",
TenantID: "acme-corp",
Key: "llama-3.1-8b",
StorageClass: sdk.StorageClassHot,
})
if err != nil {
panic(err)
}
fmt.Printf("Uploaded: %s\n", obj.ID)
// Download the object
reader, metadata, err := client.Download(ctx, obj.ID, "acme-corp")
if err != nil {
panic(err)
}
defer reader.Close()
output, _ := os.Create("downloaded.bin")
defer output.Close()
io.Copy(output, reader)
fmt.Printf("Downloaded %d bytes\n", metadata.SizeBytes)
}The SDK supports three authentication methods:
client, err := sdk.NewClientWithBearerToken(
"http://localhost:8080",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
)client, err := sdk.NewClientWithAPIKey(
"http://localhost:8080",
"your-api-key",
)client, err := sdk.NewClientWithoutAuth("http://localhost:8080")Upload an object with streaming support:
file, _ := os.Open("data.bin")
defer file.Close()
obj, err := client.Upload(ctx, file, sdk.UploadOptions{
Namespace: "models",
TenantID: "acme-corp",
Key: "my-model", // Optional
StorageClass: sdk.StorageClassHot, // Optional, defaults to "hot"
})Download by object ID:
reader, metadata, err := client.Download(ctx, objectID, tenantID)
if err != nil {
return err
}
defer reader.Close()
// Stream to file
output, _ := os.Create("output.bin")
defer output.Close()
io.Copy(output, reader)Or download directly to a writer:
output, _ := os.Create("output.bin")
defer output.Close()
metadata, err := client.DownloadToWriter(ctx, objectID, tenantID, output)Download by key:
reader, metadata, err := client.DownloadByKey(ctx, "models", "acme-corp", "llama-3.1-8b")List objects with pagination:
limit := int64(50)
offset := int64(0)
response, err := client.List(ctx, sdk.ListOptions{
Namespace: "models",
TenantID: "acme-corp",
Limit: &limit,
Offset: &offset,
})
for _, obj := range response.Objects {
fmt.Printf("%s: %s\n", obj.ID, obj.Key)
}Delete an object:
err := client.Delete(ctx, objectID, tenantID)
if err != nil {
return err
}health, err := client.Health(ctx)
fmt.Printf("Status: %s\n", health.Status)
readiness, err := client.Readiness(ctx)
fmt.Printf("Database: %s\n", readiness.Database)The SDK provides structured error handling:
obj, err := client.Upload(ctx, file, opts)
if err != nil {
if sdk.IsNotFound(err) {
fmt.Println("Object not found")
} else if sdk.IsUnauthorized(err) {
fmt.Println("Authentication failed")
} else if sdk.IsBadRequest(err) {
fmt.Println("Invalid request")
} else {
fmt.Printf("Error: %v\n", err)
}
return
}Error types include:
Code: Error code (e.g., "NOT_FOUND", "UNAUTHORIZED")Message: Human-readable messageDetails: Additional error detailsStatus: HTTP status code
httpClient := &http.Client{
Timeout: 60 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
},
}
client, err := sdk.NewClient(sdk.Config{
BaseURL: "http://localhost:8080",
HTTPClient: httpClient,
AuthToken: "your-token",
AuthType: sdk.AuthTypeBearer,
})ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
obj, err := client.Upload(ctx, file, opts)The SDK supports streaming for efficient memory usage:
// Upload from a reader (e.g., from another API)
reader := getDataFromSomewhere()
obj, err := client.Upload(ctx, reader, opts)
// Download to a writer (e.g., to another API)
writer := sendDataSomewhere()
metadata, err := client.DownloadToWriter(ctx, objectID, tenantID, writer)sdk.StorageClassHot: Frequently accessed data (default)sdk.StorageClassCold: Archive data, lower cost
Objects transition through states:
WRITING: Upload in progressCOMMITTED: Available for downloadDELETING: Deletion requestedDELETED: Physically removed
Only COMMITTED objects are visible in list/download operations.
- Always use context: Pass context for cancellation and timeouts
- Close readers: Always close download readers to free resources
- Handle errors: Check errors and use helper functions for common cases
- Stream large files: Use streaming for files > 100MB
- Set appropriate storage class: Use "cold" for archives, "hot" for active data
See the examples/ directory for complete examples:
upload_example.go: Upload a filedownload_example.go: Download and save a filelist_example.go: List and paginate objectsstreaming_example.go: Stream data between services
See LICENSE file for details.
Contributions welcome! Please see CONTRIBUTING.md for guidelines.