v1 - Experimental: This library is in its initial release phase. APIs may change in future versions.
Idiomatic Go helpers for AWS SDK v2, designed for production use.
- No global state: All clients are explicitly created and passed
- Context-aware: All operations accept
context.Contextfor cancellation and timeouts - Testable: Interfaces enable mocking without real AWS calls
- Explicit configuration: No magic; all settings are visible
- Safe defaults: Sensible retry policies out of the box
- Minimal abstraction: Helpers augment the SDK, not replace it
go get github.com/alessiosavi/GoGPUtils/aws| Package | Service | Description |
|---|---|---|
aws |
Core | Configuration, errors, common utilities |
aws/s3 |
S3 | Object storage operations |
aws/dynamodb |
DynamoDB | NoSQL database operations |
aws/sqs |
SQS | Message queue operations |
aws/secretsmanager |
Secrets Manager | Secret storage and retrieval |
aws/ssm |
SSM Parameter Store | Configuration parameter storage |
aws/lambda |
Lambda | Serverless function operations |
package main
import (
"context"
"log"
"github.com/alessiosavi/GoGPUtils/aws"
"github.com/alessiosavi/GoGPUtils/aws/s3"
)
func main() {
ctx := context.Background()
// Load AWS configuration
cfg, err := aws.LoadConfig(ctx,
aws.WithRegion("us-west-2"),
aws.WithRetryMaxAttempts(5),
)
if err != nil {
log.Fatal(err)
}
// Create S3 client
s3Client, err := s3.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
// Use the client
data, err := s3Client.GetObject(ctx, "my-bucket", "my-key")
if err != nil {
log.Fatal(err)
}
log.Printf("Downloaded %d bytes", len(data))
}// Upload an object
err := client.PutObject(ctx, "bucket", "key", []byte("content"),
s3.WithContentType("text/plain"),
s3.WithStorageClass(types.StorageClassStandardIa),
)
// Download an object
data, err := client.GetObject(ctx, "bucket", "key")
// List objects with prefix
objects, err := client.ListObjects(ctx, "bucket",
s3.WithPrefix("logs/"),
s3.WithMaxKeys(100),
)
// Check if object exists
exists, err := client.ObjectExists(ctx, "bucket", "key")
// Copy/Move objects
err := client.CopyObject(ctx, "src-bucket", "src-key", "dst-bucket", "dst-key")
err := client.MoveObject(ctx, "bucket", "old-key", "bucket", "new-key")
// Parse S3 URIs
bucket, key, err := s3.ParseS3Path("s3://my-bucket/path/to/file.txt")// Define your item struct
type User struct {
ID string `dynamodbav:"pk"`
Email string `dynamodbav:"email"`
Name string `dynamodbav:"name"`
}
// Put an item
user := User{ID: "user-123", Email: "alice@example.com", Name: "Alice"}
err := client.PutItem(ctx, "users", user)
// Get an item
var result User
err := client.GetItem(ctx, "users", dynamodb.Key{"pk": "user-123"}, &result)
// Query items
keyExpr := expression.Key("pk").Equal(expression.Value("user-123"))
result, err := client.Query(ctx, "users", keyExpr)
var users []User
err = result.Unmarshal(&users)
// Batch operations
items := []any{user1, user2, user3}
unprocessed, err := client.BatchWriteItems(ctx, "users", items)// Send a message
msgID, err := client.SendMessage(ctx, queueURL, "Hello World",
sqs.WithDelaySeconds(60),
)
// Receive messages with long polling
messages, err := client.ReceiveMessages(ctx, queueURL,
sqs.WithMaxMessages(10),
sqs.WithWaitTimeSeconds(20),
)
// Process and delete
for _, msg := range messages {
process(msg.Body)
client.DeleteMessage(ctx, queueURL, msg.ReceiptHandle)
}
// Resolve queue name to URL
queueURL, err := client.GetQueueURL(ctx, "my-queue")// Get a secret as string
apiKey, err := client.GetSecretString(ctx, "api-key")
// Get and unmarshal JSON secret
type DBConfig struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
}
var config DBConfig
err := client.GetSecretJSON(ctx, "db-credentials", &config)
// List all secrets
secrets, err := client.ListSecrets(ctx,
secretsmanager.WithNameFilter("prod/"),
)// Get a parameter (auto-decrypts SecureString)
value, err := client.GetParameter(ctx, "/app/config/database_url")
// Get multiple parameters
values, invalid, err := client.GetParameters(ctx, []string{
"/app/config/a",
"/app/config/b",
})
// List parameters by path
params, err := client.ListParametersByPath(ctx, "/app/config/",
ssm.WithRecursive(true),
)
// Put a parameter
err := client.PutParameter(ctx, "/app/config/key", "value",
ssm.WithParameterType(types.ParameterTypeSecureString),
ssm.WithOverwrite(true),
)// Invoke synchronously
result, err := client.Invoke(ctx, "my-function", payload)
if result.HasError() {
log.Printf("Function error: %s", result.FunctionError)
}
var response MyResponse
err = result.Unmarshal(&response)
// Invoke asynchronously
err := client.InvokeAsync(ctx, "my-function", payload)
// List functions
functions, err := client.ListFunctions(ctx)
// Deploy from S3
err := client.DeployFromS3(ctx, "my-function", "bucket", "code.zip")
// Deploy from local ZIP
err := client.DeployFromZip(ctx, "my-function", "function.zip")All clients accept interfaces that can be mocked for testing:
package mypackage_test
import (
"context"
"testing"
"github.com/alessiosavi/GoGPUtils/aws/s3"
)
type mockS3API struct {
s3.API
getObjectData []byte
}
func (m *mockS3API) GetObject(ctx context.Context, params *s3sdk.GetObjectInput, optFns ...func(*s3sdk.Options)) (*s3sdk.GetObjectOutput, error) {
return &s3sdk.GetObjectOutput{
Body: io.NopCloser(bytes.NewReader(m.getObjectData)),
}, nil
}
func TestMyFunction(t *testing.T) {
mock := &mockS3API{getObjectData: []byte("test content")}
client := s3.NewClientWithAPI(mock, nil, nil)
data, err := client.GetObject(context.Background(), "bucket", "key")
if err != nil {
t.Fatal(err)
}
if string(data) != "test content" {
t.Errorf("unexpected data: %s", data)
}
}cfg, err := aws.LoadConfig(ctx,
aws.WithRegion("us-west-2"), // Set region
aws.WithProfile("production"), // Use named profile
aws.WithRetryMaxAttempts(5), // Set retry attempts
aws.WithRetryMode(aws.RetryModeAdaptive), // Set retry mode
aws.WithEndpoint("http://localhost:4566"), // Custom endpoint (LocalStack)
aws.WithCredentials(credsProvider), // Custom credentials
)Each service client supports additional options:
// S3 with custom part size for multipart uploads
s3Client, err := s3.NewClient(cfg,
s3.WithUploaderConcurrency(10),
s3.WithDownloaderConcurrency(10),
s3.WithPartSize(10 * 1024 * 1024), // 10 MB parts
)All errors are wrapped with context:
data, err := client.GetObject(ctx, "bucket", "key")
if err != nil {
// Check for specific errors
if errors.Is(err, s3.ErrObjectNotFound) {
// Handle not found
}
// Or check the operation error type
var opErr *aws.OperationError
if errors.As(err, &opErr) {
log.Printf("Service: %s, Operation: %s", opErr.Service, opErr.Operation)
}
}If migrating from the old AWS helpers (commit b7a0843), note these changes:
- No global clients: Create clients explicitly with
NewClient(cfg) - No panics: All errors are returned, never panic
- Context required: All operations require
context.Context - Explicit imports: Import each service package separately
- Interface-based: Use
NewClientWithAPI()for testing
import "github.com/alessiosavi/GoGPUtils/aws/S3"
// Global client initialized in init()
data, err := S3utils.GetObject("bucket", "key")import (
"github.com/alessiosavi/GoGPUtils/aws"
"github.com/alessiosavi/GoGPUtils/aws/s3"
)
cfg, _ := aws.LoadConfig(ctx)
client, _ := s3.NewClient(cfg)
data, err := client.GetObject(ctx, "bucket", "key")- Explicit over implicit: No hidden state or magic configuration
- Composition over inheritance: Small, focused packages
- Errors over panics: All failures return errors
- Interfaces for testing: Every client can be mocked
- Context everywhere: Cancellation and timeouts are first-class
- Safe defaults: Sensible retry and timeout configurations
- Minimal surface: Only expose what's necessary
Contributions are welcome! Please ensure:
- All code follows Go conventions (run
gofmt) - Tests cover new functionality
- Documentation is updated
- No breaking changes to public API
MIT License - see LICENSE file for details.