Learn common and recommended ways of programming with the AWS SDK for Go in your applications.
Constructing a Service ClientService clients can be constructed using either the New
or NewFromConfig
functions available in service client's Go package. Each function will return a Client
struct type containing the methods for invoking the service APIs. The New
and NewFromConfig
each provide the same set of configurable options for constructing a service client, but provide slightly different construction patterns that we will look at in the following sections.
NewFromConfig
function provides a consistent interface for constructing service clients using the aws.Config. An aws.Config
can be loaded using the config.LoadDefaultConfig. For more information on constructing an aws.Config
, see Configure the SDK. The following example shows how to construct an Amazon S3 service client using the aws.Config
and the NewFromConfig
function:
import "context"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic(err)
}
client := s3.NewFromConfig(cfg)
Overriding Configuration
NewFromConfig
can take one or more functional arguments that can mutate a client's configuration Options
struct. This allows you to make specific overrides such as changing the Region, or modifying service specific options such as Amazon S3 UseAccelerate
option. For example:
import "context"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic(err)
}
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.Region = "us-west-2"
o.UseAccelerate = true
})
Overrides to the client Options
value is determined by the order that the functional arguments are given to NewFromConfig
.
New
is considered a more advanced form of client construction. We recommend you use NewFromConfig
for client construction, as it allows construction using the aws.Config
struct. This removes the need to construct an Options
struct instance for each service client your application requires.
New
function is a client constructor provides an interface for constructing clients using only the client packages Options
struct for defining the client's configuration options. For example, to construct Amazon S3 client using New
:
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/credentials"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
client := s3.New(s3.Options{
Region: "us-west-2",
Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
})
Overriding Configuration
New
can take one or more functional arguments that can mutate a client's configuration Options
struct. This allows you to make specific overrides such as changing the Region or modifying service specific options such as Amazon S3 UseAccelerate
option. For example:
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/credentials"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
options := s3.Options{
Region: "us-west-2",
Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")),
}
client := s3.New(options, func(o *s3.Options) {
o.Region = "us-east-1"
o.UseAccelerate = true
})
Overrides to the client Options
value is determined by the order that the functional arguments are given to New
.
After you have a service client instance, you can use it to call a service's operations. For example, to call the Amazon S3 GetObject
operation:
response, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
Bucket: aws.String("amzn-s3-demo-bucket
"),
Key: aws.String("obj-key
"),
})
When you call a service operation, the SDK synchronously validates the input, serializes the request, signs it with your credentials, sends it to AWS, and then deserializes a response or an error. In most cases, you can call service operations directly. Each service operation client method will return an operation response struct, and an error interface type. You should always check error
type to determine if an error occurred before attempting to access the service operation's response struct.
Each service operation method takes a context.Context value that can be used for setting request deadlines that will be honored by the SDK. In addition, each service operation will take a <OperationName>Input
struct found in the service's respective Go package. You pass in API input parameters using the operation input struct.
Operation input structures can have input parameters such as the standard Go numerics, boolean, string, map, and list types. In more complex API operations a service might have more complex modeling of input parameters. These other types such as service specific structures and enum values are found in the service's types
Go package.
In addition, services might distinguish between the default value of a Go type and whether the value was set or not by the user. In these cases, input parameters might require you to pass a pointer reference to the type in question. For standard Go types like numerics, boolean, and string there are <Type>
and From<Type>
convenience functions available in the aws to ease this conversion. For example, aws.String can be used to convert a string
to a *string
type for input parameters that require a pointer to a string. Inversely, aws.ToString can be used to transform a *string
to a string
while providing protection from dereferencing a nil pointer. The To<Type>
functions are helpful when handling service responses.
Let's look at an example of how we can use an Amazon S3 client to call the GetObject
API, and construct our input using the types
package, and aws.<Type>
helpers.
import "context"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
import "github.com/aws/aws-sdk-go-v2/service/s3/types"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic(err)
}
client := s3.NewFromConfig(cfg)
resp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
Bucket: aws.String("amzn-s3-demo-bucket
"),
Key: aws.String("keyName
"),
RequestPayer: types.RequestPayerRequester,
})
Overriding Client Options For Operation Call
Similar to how client operation options can be modified during construction of a client using functional arguments, the client options can be modified at the time the operation method is called by providing one or more functional arguments to the service operation method. This action is concurrency safe and will not affect other concurrent operations on the client.
For example, to override the client region from "us-west-2" to "us-east-1":
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
if err != nil {
log.Printf("error: %v", err)
return
}
client := s3.NewFromConfig(cfg)
params := &s3.GetObjectInput{
// ...
}
resp, err := client.GetObject(context.TODO(), params, func(o *Options) {
o.Region = "us-east-1"
})
Handling Operation Responses
Each service operation has an associated output struct that contains the service's operation response members. The output struct follows the following naming pattern <OperationName>Output
. Some operations might have no members defined for their operation output. After calling a service operation, the return error
argument type should always be checked to determine if an error occurred while invoking the service operation. Errors returned can range from client-side input validation errors to service-side error responses returned to the client. The operation's output struct should not be accessed in the event that a non-nil error is returned by the client.
For example, to log an operation error and prematurely return from the calling function:
response, err := client.GetObject(context.TODO())
if err != nil {
log.Printf("GetObject error: %v", err)
return
}
For more information on error handling, including how to inspect for specific error types, see TODO
Responses withio.ReadCloser
Some API operations return a response struct that contain an output member that is an io.ReadCloser
. This will be the case for API operations that expose some element of their output in the body of the HTTP response itself.
For example, Amazon S3 GetObject
operation returns a response whose Body
member is an io.ReadCloser
for accessing the object payload.
You MUST ALWAYS Close()
any io.ReadCloser
output members, regardless of whether you have consumed its content. Failure to do so can leak resources and potentially create issues with reading response bodies for operations called in the future.
resp, err := s3svc.GetObject(context.TODO(), &s3.GetObjectInput{...})
if err != nil {
// handle error
return
}
// Make sure to always close the response Body when finished
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
if err := decoder.Decode(&myStruct); err != nil {
// handle error
return
}
Response Metadata
All service operation output structs include a ResultMetadata
member of type middleware.Metadata. middleware.Metadata
is used by the SDK middleware to provide additional information from a service response that is not modeled by the service. This includes metadata like the RequestID
. For example, to retrieve the RequestID
associated with a service response to assist AWS Support in troubleshooting a request:
import "fmt"
import "log"
import "github.com/aws/aws-sdk-go-v2/aws/middleware"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ..
resp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
// ...
})
if err != nil {
log.Printf("error: %v", err)
return
}
requestID, ok := middleware.GetRequestIDMetadata(resp.ResultMetadata)
if !ok {
fmt.Println("RequestID not included with request")
}
fmt.Printf("RequestID: %s\n", requestID)
Concurrently Using Service Clients
You can create goroutines that concurrently use the same service client to send multiple requests. You can use a service client with as many goroutines as you want.
In the following example, an Amazon S3 service client is used in multiple goroutines. This example concurrently uploads two objects to an Amazon S3 bucket.
import "context"
import "log"
import "strings"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
client := s3.NewFromConfig(cfg)
type result struct {
Output *s3.PutObjectOutput
Err error
}
results := make(chan result, 2)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
output, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String("amzn-s3-demo-bucket
"),
Key: aws.String("foo
"),
Body: strings.NewReader("foo body content"),
})
results <- result{Output: output, Err: err}
}()
go func() {
defer wg.Done()
output, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String("amzn-s3-demo-bucket
"),
Key: aws.String("bar
"),
Body: strings.NewReader("bar body content"),
})
results <- result{Output: output, Err: err}
}()
wg.Wait()
close(results)
for result := range results {
if result.Err != nil {
log.Printf("error: %v", result.Err)
continue
}
fmt.Printf("etag: %v", aws.ToString(result.Output.ETag))
}
Using Operation Paginators
Typically, when you retrieve a list of items, you might need to check the output struct for a token or marker to confirm whether the AWS service returned all results from your request. If the token or marker is present, you use it to request the next page of results. Instead of managing these tokens or markers, you can use the service package's available paginator types.
Paginator helpers are available for supported service operations, and can be found in the service client's Go package. To construct a paginator for a supported operation, use the New<OperationName>Paginator
function. Paginator construct functions take the service Client
, the operation's <OperationName>Input
input parameters, and an optional set of functional arguments allowing you to configure other optional paginator settings.
The returned operation paginator type provides a convenient way to iterate over a paginated operation until you have reached the last page, or you have found the item(s) that your application was searching for. A paginator type has two methods: HasMorePages
and NextPage
. HasMorePages
returns a boolean value of true
if the first page has not been retrieved, or if additional pages available to retrieve using the operation. To retrieve the first or subsequent pages of the operation, the NextPage
operation must be called. NextPage
takes context.Context
and returns the operation output and any corresponding error. Like the client operation method return parameters, the return error should always be checked before attempting to use the returned response structure. See Handling Operation Responses.
The following example uses the ListObjectsV2
paginator to list up to three pages of object keys from the ListObjectV2
operation. Each page consists of up to 10 keys, which is defined by the Limit
paginator option.
import "context"
import "log"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/service/s3"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
client := s3.NewFromConfig(cfg)
params := &s3.ListObjectsV2Input{
Bucket: aws.String("amzn-s3-demo-bucket
"),
}
paginator := s3.NewListObjectsV2Paginator(client, params, func(o *s3.ListObjectsV2PaginatorOptions) {
o.Limit = 10
})
pageNum := 0
for paginator.HasMorePages() && pageNum < 3 {
output, err := paginator.NextPage(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
for _, value := range output.Contents {
fmt.Println(*value.Key)
}
pageNum++
}
Similar to client operation method, the client options like the request Region can be modified by providing one or more functional arguments to NextPage
. For more information about overriding client options when calling an operation, see Overriding Client Options For Operation Call.
When interacting with AWS APIs that are asynchronous, you often need to wait for a particular resource to become available in order to perform further actions on it.
For example, the Amazon DynamoDB CreateTable
API returns immediately with a TableStatus of CREATING, and you can't invoke read or write operations until the table status has been transitioned to ACTIVE
.
Writing logic to continuously poll the table status can be cumbersome and error-prone. The waiters help take the complexity out of it and are simple APIs that handle the polling task for you.
For example, you can use waiters to poll if a DynamoDB table is created and ready for a write operation.
import "context"
import "fmt"
import "log"
import "time"
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/dynamodb"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
client := dynamodb.NewFromConfig(cfg)
// we create a waiter instance by directly passing in a client
// that satisfies the waiters client Interface.
waiter := dynamodb.NewTableExistsWaiter(client)
// params is the input to api operation used by the waiter
params := &dynamodb.DescribeTableInput {
TableName: aws.String("test-table")
}
// maxWaitTime is the maximum wait time, the waiter will wait for
// the resource status.
maxWaitTime := 5 * time.Minutes
// Wait will poll until it gets the resource status, or max wait time
// expires.
err := waiter.Wait(context.TODO(), params, maxWaitTime)
if err != nil {
log.Printf("error: %v", err)
return
}
fmt.Println("Dynamodb table is now ready for write operations")
Overriding waiter configuration
By default, the SDK uses the minimum delay and maximum delay value configured with optimal values defined by AWS services for different APIs. You can override waiter configuration by providing functional options during waiter construction, or when invoking a waiter operation.
For example, to override waiter configuration during waiter construction
import "context"
import "fmt"
import "log"
import "time"
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/dynamodb"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
client := dynamodb.NewFromConfig(cfg)
// we create a waiter instance by directly passing in a client
// that satisfies the waiters client Interface.
waiter := dynamodb.NewTableExistsWaiter(client, func (o *dynamodb.TableExistsWaiterOptions) {
// override minimum delay to 10 seconds
o.MinDelay = 10 * time.Second
// override maximum default delay to 300 seconds
o.MaxDelay = 300 * time.Second
})
The Wait
function on each waiter also takes in functional options. Similar to the above example, you can override waiter configuration per Wait
request.
// params is the input to api operation used by the waiter
params := &dynamodb.DescribeTableInput {
TableName: aws.String("test-table")
}
// maxWaitTime is the maximum wait time, the waiter will wait for
// the resource status.
maxWaitTime := 5 * time.Minutes
// Wait will poll until it gets the resource status, or max wait time
// expires.
err := waiter.Wait(context.TODO(), params, maxWaitTime, func (o *dynamodb.TableExistsWaiterOptions) {
// override minimum delay to 5 seconds
o.MinDelay = 5 * time.Second
// override maximum default delay to 120 seconds
o.MaxDelay = 120 * time.Second
})
if err != nil {
log.Printf("error: %v", err)
return
}
fmt.Println("Dynamodb table is now ready for write operations")
Advanced waiter configuration overrides
You can additionally customize the waiter default behavior by providing a custom retryable function. The waiter-specific options also provides APIOptions
to customize operation middlewares.
For example, to configure advanced waiter overrides.
import "context"
import "fmt"
import "log"
import "time"
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/dynamodb"
import "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
// ...
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Printf("error: %v", err)
return
}
client := dynamodb.NewFromConfig(cfg)
// custom retryable defines if a waiter state is retryable or a terminal state.
// For example purposes, we will configure the waiter to not wait
// if table status is returned as `UPDATING`
customRetryable := func(ctx context.Context, params *dynamodb.DescribeTableInput,
output *dynamodb.DescribeTableOutput, err error) (bool, error) {
if output.Table != nil {
if output.Table.TableStatus == types.TableStatusUpdating {
// if table status is `UPDATING`, no need to wait
return false, nil
}
}
}
// we create a waiter instance by directly passing in a client
// that satisfies the waiters client Interface.
waiter := dynamodb.NewTableExistsWaiter(client, func (o *dynamodb.TableExistsWaiterOptions) {
// override the service defined waiter-behavior
o.Retryable = customRetryable
})
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4