Documentation
¶
Overview ¶
Package blwa provides a batteries-included framework for building HTTP services on AWS Lambda Web Adapter (LWA).
Overview ¶
blwa handles the boilerplate of setting up an HTTP server optimized for Lambda: environment parsing, structured logging, OpenTelemetry tracing, AWS SDK clients, and graceful shutdown. A complete application can be created in a single call:
blwa.NewApp[Env](func(m *blwa.Mux, h *Handlers) {
m.HandleFunc("GET /items", h.ListItems)
m.HandleFunc("GET /items/{id}", h.GetItem, "get-item")
},
blwa.WithAWSClient(dynamodb.NewFromConfig),
blwa.WithFx(fx.Provide(NewHandlers)),
).Run()
Environment Configuration ¶
Define your environment by embedding BaseEnvironment:
type Env struct {
blwa.BaseEnvironment
MainTableName string `env:"MAIN_TABLE_NAME,required"`
}
BaseEnvironment provides the following environment variables:
| Variable | Required | Default | Description | |-------------------------------|----------|---------|------------------------------------------------------| | AWS_LWA_PORT | Yes | - | Port the HTTP server listens on | | AWS_LWA_READINESS_CHECK_PATH | Yes | - | Health check endpoint path for LWA readiness | | AWS_REGION | Yes | - | AWS region (set automatically by Lambda runtime) | | BW_SERVICE_NAME | Yes | - | Service name for logging and tracing | | BW_PRIMARY_REGION | Yes | - | Primary deployment region (injected by CDK) | | BW_LOG_LEVEL | No | info | Log level (debug, info, warn, error) | | BW_OTEL_EXPORTER | No | stdout | Trace exporter: "stdout" or "xrayudp" | | BW_GATEWAY_ACCESS_LOG_GROUP | No | - | API Gateway access log group for X-Ray correlation |
The AWS_LWA_* variables match the official Lambda Web Adapter configuration, so values you set for LWA are automatically picked up by blwa. AWS_REGION is set automatically by the Lambda runtime, while BW_PRIMARY_REGION is injected by the bwcdklwalambda CDK construct.
Runtime ¶
Runtime provides access to app-scoped dependencies and should be injected into handler constructors via fx. This follows idiomatic Go patterns where app-level dependencies are passed explicitly, not pulled from context.
Runtime provides:
- Runtime.Env returns the typed environment configuration
- Runtime.Reverse generates URLs for named routes
- Runtime.Secret retrieves secrets from AWS Secrets Manager
Example handler struct with Runtime:
type Handlers struct {
rt *blwa.Runtime[Env]
dynamo *dynamodb.Client
}
func NewHandlers(rt *blwa.Runtime[Env], dynamo *dynamodb.Client) *Handlers {
return &Handlers{rt: rt, dynamo: dynamo}
}
func (h *Handlers) GetItem(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
env := h.rt.Env() // typed environment
url, _ := h.rt.Reverse("get-item", id) // URL generation
h.dynamo.GetItem(ctx, ...) // direct client access
// ...
}
Secrets ¶
Runtime.Secret retrieves secrets from AWS Secrets Manager with caching. Secrets are fetched per-request to support rotation without redeployment.
// Raw string secret
apiKey, err := h.rt.Secret(ctx, "my-api-key-secret")
// JSON secret with nested path extraction (uses gjson syntax)
// e.g., secret contains: {"database": {"host": "...", "password": "secret123"}}
password, err := h.rt.Secret(ctx, "my-db-credentials", "database.password")
Context Functions ¶
Request-scoped values are accessed through context functions:
- Log returns a trace-correlated zap logger
- Span returns the current OpenTelemetry span for custom instrumentation
- LWA retrieves Lambda execution context (request ID, deadline, etc.)
Example handler using context functions:
func (h *Handlers) GetItem(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx) // trace-correlated logger
env := h.rt.Env() // from Runtime, not context
blwa.Span(ctx).AddEvent("fetching item")
// ... handler logic
}
Tracing ¶
OpenTelemetry tracing is configured automatically based on BW_OTEL_EXPORTER:
- "stdout" (default): Pretty-printed spans for local development
- "xrayudp": X-Ray UDP exporter for Lambda with proper trace ID format
The tracer provider and propagator are injected explicitly (no globals), allowing for proper testing and isolation.
When BW_GATEWAY_ACCESS_LOG_GROUP is set (injected automatically by bwcdkrestgateway), the log group is added to trace segments via the aws.log.group.names resource attribute. This enables X-Ray's "View Logs" feature to query API Gateway access logs alongside Lambda function logs.
AWS Clients ¶
AWS SDK v2 clients are registered with WithAWSClient and injected directly into handler constructors via fx. This eliminates reflection and makes dependencies explicit in the type system.
Local Region Clients (Default) ¶
For clients that should use the Lambda's local region (AWS_REGION), register the client factory directly. The client type is injected as-is:
// Registration
blwa.WithAWSClient(func(cfg aws.Config) *dynamodb.Client {
return dynamodb.NewFromConfig(cfg)
})
// Injection - receives *dynamodb.Client directly
func NewHandlers(dynamo *dynamodb.Client) *Handlers {
return &Handlers{dynamo: dynamo}
}
Primary Region Clients ¶
For clients that must target the primary deployment region (BW_PRIMARY_REGION), wrap the client with Primary to make the region explicit in the type:
// Registration
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion())
// Injection - receives *blwa.Primary[ssm.Client]
func NewHandlers(ssm *blwa.Primary[ssm.Client]) *Handlers {
return &Handlers{ssm: ssm}
}
// Usage - access via .Client field
h.ssm.Client.GetParameter(ctx, ...)
Common use cases for primary region clients:
- Generating S3 presigned URLs that work across all regions
- Publishing to centralized SQS queues or SNS topics
- Accessing primary-region-only resources (e.g., certain AWS services)
Fixed Region Clients ¶
For clients that must target a specific region, wrap with InRegion:
// Registration
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[s3.Client] {
return blwa.NewInRegion(s3.NewFromConfig(cfg), "eu-central-1")
}, blwa.ForRegion("eu-central-1"))
// Injection - receives *blwa.InRegion[s3.Client]
func NewHandlers(s3 *blwa.InRegion[s3.Client]) *Handlers {
return &Handlers{s3: s3}
}
// Usage - access client and region via fields
h.s3.Client.PutObject(ctx, ...)
log.Info("uploading", zap.String("region", h.s3.Region))
Common use cases for fixed region clients:
- Accessing S3 buckets in specific regions
- Targeting SQS queues in particular regions
- Cross-region replication operations
Multiple Region Types Together ¶
A handler can inject clients for different regions simultaneously:
type Handlers struct {
dynamo *dynamodb.Client // local region
ssm *blwa.Primary[ssm.Client] // primary region
s3 *blwa.InRegion[s3.Client] // fixed region
}
func NewHandlers(
dynamo *dynamodb.Client,
ssm *blwa.Primary[ssm.Client],
s3 *blwa.InRegion[s3.Client],
) *Handlers {
return &Handlers{dynamo: dynamo, ssm: ssm, s3: s3}
}
Health Checks ¶
A health endpoint is automatically registered at AWS_LWA_READINESS_CHECK_PATH (required env var). Lambda Web Adapter uses this to determine readiness. Customize with WithHealthHandler.
Dependency Injection ¶
blwa uses go.uber.org/fx for dependency injection. Add custom providers with WithFx:
blwa.WithFx(
fx.Provide(NewHandlers),
fx.Provide(NewRepository),
)
Example ¶
Example demonstrates a complete blwa application with local region AWS clients. AWS clients are injected directly into handler constructors via fx.
package main
import (
"context"
"encoding/json"
"net/http"
"github.com/advdv/bhttp"
"github.com/advdv/bhttp/blwa"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"go.uber.org/fx"
"go.uber.org/zap"
)
// Env defines the environment variables for the application.
// Embed blwa.BaseEnvironment to get the required LWA fields.
type Env struct {
blwa.BaseEnvironment
MainTableName string `env:"MAIN_TABLE_NAME,required"`
}
// ItemHandlers contains the HTTP handlers for item operations.
// Dependencies are injected via the constructor, including AWS clients.
type ItemHandlers struct {
rt *blwa.Runtime[Env]
dynamo *dynamodb.Client
}
func NewItemHandlers(rt *blwa.Runtime[Env], dynamo *dynamodb.Client) *ItemHandlers {
return &ItemHandlers{rt: rt, dynamo: dynamo}
}
// ListItems returns all items from the database.
// Demonstrates: Log for trace-correlated logging, Runtime.Env for configuration access.
func (h *ItemHandlers) ListItems(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx)
env := h.rt.Env()
log.Info("listing items from table",
zap.String("table", env.MainTableName))
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]any{
"table": env.MainTableName,
"items": []string{"item-1", "item-2"},
})
}
// GetItem returns a single item by ID.
// Demonstrates: Span for adding trace events, Runtime.Reverse for URL generation.
func (h *ItemHandlers) GetItem(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
id := r.PathValue("id")
span := blwa.Span(ctx)
span.AddEvent("fetching item")
selfURL, _ := h.rt.Reverse("get-item", id)
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]any{
"id": id,
"self": selfURL,
})
}
// CreateItem creates a new item in DynamoDB.
// Demonstrates: Direct AWS client injection, LWA for Lambda context.
func (h *ItemHandlers) CreateItem(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx)
if lwa := blwa.LWA(ctx); lwa != nil {
log.Info("lambda context",
zap.String("request_id", lwa.RequestID),
zap.Duration("remaining", lwa.RemainingTime()),
)
}
_ = h.dynamo
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
return json.NewEncoder(w).Encode(map[string]string{
"id": "new-item-123",
"status": "created",
})
}
func main() {
blwa.NewApp[Env](
func(m *blwa.Mux, h *ItemHandlers) {
m.HandleFunc("GET /items", h.ListItems)
m.HandleFunc("GET /items/{id}", h.GetItem, "get-item")
m.HandleFunc("POST /items", h.CreateItem)
},
// Local region DynamoDB client - injected directly as *dynamodb.Client
blwa.WithAWSClient(func(cfg aws.Config) *dynamodb.Client {
return dynamodb.NewFromConfig(cfg)
}),
blwa.WithFx(fx.Provide(NewItemHandlers)),
).Run()
}
Example (FixedRegion) ¶
Example_fixedRegion demonstrates fixed region AWS client injection. Use InRegion[T] wrapper when you need to access resources in a specific region (e.g., S3 buckets that must be in a particular region).
package main
import (
"context"
"encoding/json"
"net/http"
"github.com/advdv/bhttp"
"github.com/advdv/bhttp/blwa"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"go.uber.org/fx"
"go.uber.org/zap"
)
// Env defines the environment variables for the application.
// Embed blwa.BaseEnvironment to get the required LWA fields.
type Env struct {
blwa.BaseEnvironment
MainTableName string `env:"MAIN_TABLE_NAME,required"`
}
// UploadHandlers demonstrates fixed region client injection.
type UploadHandlers struct {
rt *blwa.Runtime[Env]
s3 *blwa.InRegion[s3.Client]
}
func NewUploadHandlers(rt *blwa.Runtime[Env], s3 *blwa.InRegion[s3.Client]) *UploadHandlers {
return &UploadHandlers{rt: rt, s3: s3}
}
// Upload uploads a file to a fixed-region S3 bucket.
// Demonstrates: Fixed region client injection using InRegion[T] wrapper.
func (h *UploadHandlers) Upload(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx)
log.Info("uploading to fixed region S3",
zap.String("region", h.s3.Region))
_ = h.s3.Client
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]string{
"status": "uploaded",
"region": h.s3.Region,
})
}
func main() {
blwa.NewApp[Env](
func(m *blwa.Mux, h *UploadHandlers) {
m.HandleFunc("POST /upload", h.Upload)
},
// Fixed region S3 client - wrapped with InRegion[T]
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[s3.Client] {
return blwa.NewInRegion(s3.NewFromConfig(cfg), "eu-central-1")
}, blwa.ForRegion("eu-central-1")),
blwa.WithFx(fx.Provide(NewUploadHandlers)),
).Run()
}
Example (MultiRegion) ¶
Example_multiRegion demonstrates using all three region types together. This is a common pattern where you need: - Local region clients for low-latency data access - Primary region clients for shared configuration - Fixed region clients for specific resources
package main
import (
"context"
"encoding/json"
"net/http"
"github.com/advdv/bhttp"
"github.com/advdv/bhttp/blwa"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"go.uber.org/fx"
"go.uber.org/zap"
)
// Env defines the environment variables for the application.
// Embed blwa.BaseEnvironment to get the required LWA fields.
type Env struct {
blwa.BaseEnvironment
MainTableName string `env:"MAIN_TABLE_NAME,required"`
}
// MultiRegionHandlers demonstrates all three region types in one handler.
type MultiHandlers struct {
rt *blwa.Runtime[Env]
dynamo *dynamodb.Client
ssm *blwa.Primary[ssm.Client]
s3 *blwa.InRegion[s3.Client]
}
func NewMultiHandlers(
rt *blwa.Runtime[Env],
dynamo *dynamodb.Client,
ssm *blwa.Primary[ssm.Client],
s3 *blwa.InRegion[s3.Client],
) *MultiHandlers {
return &MultiHandlers{rt: rt, dynamo: dynamo, ssm: ssm, s3: s3}
}
func (h *MultiHandlers) Process(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx)
log.Info("processing with multi-region clients",
zap.String("s3_region", h.s3.Region))
_ = h.dynamo
_ = h.ssm.Client
_ = h.s3.Client
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]string{
"status": "processed",
"s3_region": h.s3.Region,
})
}
func main() {
blwa.NewApp[Env](
func(m *blwa.Mux, h *MultiHandlers) {
m.HandleFunc("POST /process", h.Process)
},
// Local region DynamoDB - direct injection
blwa.WithAWSClient(func(cfg aws.Config) *dynamodb.Client {
return dynamodb.NewFromConfig(cfg)
}),
// Primary region SSM - wrapped with Primary[T]
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion()),
// Fixed region S3 - wrapped with InRegion[T]
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[s3.Client] {
return blwa.NewInRegion(s3.NewFromConfig(cfg), "eu-central-1")
}, blwa.ForRegion("eu-central-1")),
blwa.WithFx(fx.Provide(NewMultiHandlers)),
).Run()
}
Example (PrimaryRegion) ¶
Example_primaryRegion demonstrates primary region AWS client injection. Use Primary[T] wrapper when you need to access resources in the primary deployment region (e.g., shared config in SSM Parameter Store).
package main
import (
"context"
"encoding/json"
"net/http"
"github.com/advdv/bhttp"
"github.com/advdv/bhttp/blwa"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"go.uber.org/fx"
)
// Env defines the environment variables for the application.
// Embed blwa.BaseEnvironment to get the required LWA fields.
type Env struct {
blwa.BaseEnvironment
MainTableName string `env:"MAIN_TABLE_NAME,required"`
}
// ConfigHandlers demonstrates primary region client injection.
type ConfigHandlers struct {
rt *blwa.Runtime[Env]
ssm *blwa.Primary[ssm.Client]
}
func NewConfigHandlers(rt *blwa.Runtime[Env], ssm *blwa.Primary[ssm.Client]) *ConfigHandlers {
return &ConfigHandlers{rt: rt, ssm: ssm}
}
// GetConfig fetches configuration from the primary region SSM Parameter Store.
// Demonstrates: Primary region client injection using Primary[T] wrapper.
func (h *ConfigHandlers) GetConfig(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx)
log.Info("fetching config from primary region SSM")
_ = h.ssm.Client
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]string{
"config": "value-from-primary-region",
})
}
func main() {
blwa.NewApp[Env](
func(m *blwa.Mux, h *ConfigHandlers) {
m.HandleFunc("GET /config", h.GetConfig)
},
// Primary region SSM client - wrapped with Primary[T]
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion()),
blwa.WithFx(fx.Provide(NewConfigHandlers)),
).Run()
}
Example (Secrets) ¶
Example_secrets demonstrates retrieving secrets from AWS Secrets Manager. Use Runtime.Secret to fetch raw string secrets or extract values from JSON secrets.
package main
import (
"context"
"encoding/json"
"net/http"
"github.com/advdv/bhttp"
"github.com/advdv/bhttp/blwa"
"go.uber.org/fx"
"go.uber.org/zap"
)
// Env defines the environment variables for the application.
// Embed blwa.BaseEnvironment to get the required LWA fields.
type Env struct {
blwa.BaseEnvironment
MainTableName string `env:"MAIN_TABLE_NAME,required"`
}
// SecretHandlers demonstrates secret retrieval from AWS Secrets Manager.
type SecretHandlers struct {
rt *blwa.Runtime[Env]
}
func NewSecretHandlers(rt *blwa.Runtime[Env]) *SecretHandlers {
return &SecretHandlers{rt: rt}
}
// Connect demonstrates retrieving secrets from AWS Secrets Manager.
// Demonstrates: Runtime.Secret for raw string secrets and JSON path extraction.
func (h *SecretHandlers) Connect(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
log := blwa.Log(ctx)
apiKey, err := h.rt.Secret(ctx, "my-api-key-secret")
if err != nil {
return err
}
dbPassword, err := h.rt.Secret(ctx, "my-db-credentials", "database.password")
if err != nil {
return err
}
log.Info("retrieved secrets",
zap.Int("api_key_len", len(apiKey)),
zap.Int("password_len", len(dbPassword)))
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]string{
"status": "connected",
})
}
func main() {
blwa.NewApp[Env](
func(m *blwa.Mux, h *SecretHandlers) {
m.HandleFunc("POST /connect", h.Connect)
},
blwa.WithFx(fx.Provide(NewSecretHandlers)),
).Run()
}
Index ¶
- Constants
- func AWSClientProvider[T any](factory func(aws.Config) T, opts ...ClientOption) fx.Option
- func Log(ctx context.Context) *zap.Logger
- func NewAWSConfig(ctx context.Context) (aws.Config, error)
- func NewLogger(env Environment) (*zap.Logger, error)
- func NewPropagator(env Environment) propagation.TextMapPropagator
- func NewServer(params ServerParams, cfg ServerConfig) *http.Server
- func NewTracerProvider(lc fx.Lifecycle, env Environment) (trace.TracerProvider, error)
- func ParseEnv[E Environment]() func() (E, error)
- func Span(ctx context.Context) trace.Span
- type AWSSecretReader
- type App
- type AppConfig
- type BaseEnvironment
- type ClientOption
- type Environment
- type InRegion
- type LWAContext
- type LWAEnvConfig
- type Mux
- type Option
- type Primary
- type Region
- type Runtime
- type RuntimeParams
- type SecretReader
- type ServerConfig
- type ServerParams
Examples ¶
Constants ¶
const LambdaMaxResponsePayloadBytes = 6*1024*1024 - 1024
LambdaMaxResponsePayloadBytes is AWS Lambda's 6 MiB limit minus 1 KiB headroom for JSON/API Gateway overhead.
Variables ¶
This section is empty.
Functions ¶
func AWSClientProvider ¶
AWSClientProvider creates an fx.Option that provides an AWS client for injection. The factory receives an aws.Config with the region already configured.
For local region clients (default), the factory returns *T directly:
blwa.WithAWSClient(func(cfg aws.Config) *dynamodb.Client {
return dynamodb.NewFromConfig(cfg)
})
For primary region clients, wrap with Primary[T]:
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion())
For fixed region clients, wrap with InRegion[T]:
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[sqs.Client] {
return blwa.NewInRegion(sqs.NewFromConfig(cfg), "us-east-1")
}, blwa.ForRegion("us-east-1"))
func NewAWSConfig ¶
NewAWSConfig loads the default AWS SDK v2 configuration.
func NewLogger ¶
func NewLogger(env Environment) (*zap.Logger, error)
NewLogger creates a zap logger configured from the environment. Uses JSON encoding suitable for CloudWatch. LOG_LEVEL controls the level (debug, info, warn, error).
func NewPropagator ¶
func NewPropagator(env Environment) propagation.TextMapPropagator
NewPropagator creates a TextMapPropagator based on the exporter type. For xrayudp: uses X-Ray propagator for AWS Lambda environments. For stdout/default: uses W3C TraceContext + Baggage composite propagator.
func NewServer ¶
func NewServer(params ServerParams, cfg ServerConfig) *http.Server
NewServer creates an HTTP server with all middleware and routing configured.
func NewTracerProvider ¶
func NewTracerProvider(lc fx.Lifecycle, env Environment) (trace.TracerProvider, error)
NewTracerProvider creates and configures the OpenTelemetry TracerProvider. Supported exporters via OTEL_EXPORTER env var: "stdout" (default), "xrayudp" (Lambda). Shutdown is handled automatically via fx.Lifecycle.
func ParseEnv ¶
func ParseEnv[E Environment]() func() (E, error)
ParseEnv parses environment variables into the given Environment type.
Types ¶
type AWSSecretReader ¶
type AWSSecretReader struct {
// contains filtered or unexported fields
}
AWSSecretReader implements SecretReader using AWS Secrets Manager caching client.
func NewAWSSecretReader ¶
func NewAWSSecretReader(cfg aws.Config) (*AWSSecretReader, error)
NewAWSSecretReader creates a new AWSSecretReader using the provided AWS config.
func (*AWSSecretReader) GetSecretString ¶
GetSecretString retrieves a secret value from AWS Secrets Manager with caching.
type App ¶
type App struct {
// contains filtered or unexported fields
}
App wraps an fx.App for lifecycle management.
func NewApp ¶
func NewApp[E Environment](routing any, opts ...Option) *App
NewApp creates a batteries-included app with dependency injection.
The routing function can request any types that are provided via fx options. At minimum, it should accept *Mux for routing.
Example:
blwa.NewApp[Env](func(m *blwa.Mux, h *Handlers) {
m.HandleFunc("GET /items", h.ListItems, "list-items")
},
blwa.WithAWSClient(func(cfg aws.Config) *dynamodb.Client {
return dynamodb.NewFromConfig(cfg)
}),
blwa.WithFx(fx.Provide(NewHandlers)),
).Run()
type AppConfig ¶
type AppConfig struct {
ServerConfig
FxOptions []fx.Option
}
AppConfig holds configuration for the app.
type BaseEnvironment ¶
type BaseEnvironment struct {
Port int `env:"AWS_LWA_PORT,required"`
ServiceName string `env:"BW_SERVICE_NAME,required"`
ReadinessCheckPath string `env:"AWS_LWA_READINESS_CHECK_PATH,required"`
LogLevel zapcore.Level `env:"BW_LOG_LEVEL" envDefault:"info"`
OtelExporter string `env:"BW_OTEL_EXPORTER" envDefault:"stdout"`
AWSRegion string `env:"AWS_REGION,required"`
PrimaryRegion string `env:"BW_PRIMARY_REGION,required"`
// GatewayAccessLogGroup is the CloudWatch Log Group name for API Gateway
// access logs. When set, traces include this log group for X-Ray log
// correlation. Injected automatically by bwcdkrestgateway.
GatewayAccessLogGroup string `env:"BW_GATEWAY_ACCESS_LOG_GROUP"`
}
BaseEnvironment contains the required LWA environment variables. Embed this in your custom environment struct.
type ClientOption ¶
type ClientOption func(*clientOptions)
ClientOption configures AWS client registration.
func ForPrimaryRegion ¶
func ForPrimaryRegion() ClientOption
ForPrimaryRegion configures the client to use the PRIMARY_REGION env var. Use this for cross-region operations that must target the primary deployment region.
The factory should return *blwa.Primary[T] to make the region explicit in the type:
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion())
func ForRegion ¶
func ForRegion(region string) ClientOption
ForRegion configures the client to use a specific fixed region.
The factory should return *blwa.InRegion[T] to make the region explicit in the type:
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[sqs.Client] {
return blwa.NewInRegion(sqs.NewFromConfig(cfg), "us-east-1")
}, blwa.ForRegion("us-east-1"))
type Environment ¶
type Environment interface {
// contains filtered or unexported methods
}
Environment defines the interface that all environment configurations must implement. Embed BaseEnvironment in your struct to satisfy this interface.
type InRegion ¶
InRegion wraps an AWS client configured for a specific fixed region. Use this when registering and injecting clients that must target a specific region.
Registration:
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[sqs.Client] {
return blwa.NewInRegion(sqs.NewFromConfig(cfg), "us-east-1")
}, blwa.ForRegion("us-east-1"))
Injection:
func NewHandlers(sqs *blwa.InRegion[sqs.Client]) *Handlers
Usage:
h.sqs.Client.SendMessage(ctx, ...) region := h.sqs.Region // "us-east-1"
func NewInRegion ¶
NewInRegion creates an InRegion wrapper for an AWS client configured for a fixed region. Use this in your client factory when registering with ForRegion():
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[sqs.Client] {
return blwa.NewInRegion(sqs.NewFromConfig(cfg), "us-east-1")
}, blwa.ForRegion("us-east-1"))
type LWAContext ¶
type LWAContext struct {
RequestID string `json:"request_id"`
Deadline int64 `json:"deadline"`
InvokedFunctionARN string `json:"invoked_function_arn"`
XRayTraceID string `json:"xray_trace_id"`
EnvConfig LWAEnvConfig `json:"env_config"`
}
LWAContext contains Lambda execution context from the x-amzn-lambda-context header.
func LWA ¶
func LWA(ctx context.Context) *LWAContext
LWA retrieves the LWAContext from the request context. Returns nil if not running in a Lambda environment.
func (*LWAContext) DeadlineTime ¶
func (lc *LWAContext) DeadlineTime() time.Time
DeadlineTime returns the Lambda invocation deadline as a time.Time.
func (*LWAContext) RemainingTime ¶
func (lc *LWAContext) RemainingTime() time.Duration
RemainingTime returns the duration until the Lambda invocation deadline.
type LWAEnvConfig ¶
type LWAEnvConfig struct {
FunctionName string `json:"function_name"`
Memory int `json:"memory"`
Version string `json:"version"`
LogGroup string `json:"log_group"`
LogStream string `json:"log_stream"`
}
LWAEnvConfig contains Lambda function environment configuration.
type Option ¶
type Option func(*AppConfig)
Option configures the App.
func WithAWSClient ¶
func WithAWSClient[T any](factory func(aws.Config) T, opts ...ClientOption) Option
WithAWSClient registers an AWS SDK v2 client for dependency injection. Clients are injected directly into handler constructors via fx.
By default, clients target the local region (AWS_REGION env var):
blwa.WithAWSClient(func(cfg aws.Config) *dynamodb.Client {
return dynamodb.NewFromConfig(cfg)
})
For primary region, wrap with Primary[T] and use ForPrimaryRegion():
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion())
For fixed region, wrap with InRegion[T] and use ForRegion():
blwa.WithAWSClient(func(cfg aws.Config) *blwa.InRegion[sqs.Client] {
return blwa.NewInRegion(sqs.NewFromConfig(cfg), "eu-west-1")
}, blwa.ForRegion("eu-west-1"))
func WithHealthHandler ¶
func WithHealthHandler(h func(http.ResponseWriter, *http.Request)) Option
WithHealthHandler sets a custom health check handler. If not set, a default handler returning 200 OK is used.
type Primary ¶
type Primary[T any] struct { Client *T }
Primary wraps an AWS client for the primary deployment region. Use this when registering and injecting clients that must target PRIMARY_REGION.
Registration:
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion())
Injection:
func NewHandlers(ssm *blwa.Primary[ssm.Client]) *Handlers
Usage:
h.ssm.Client.GetParameter(ctx, ...)
func NewPrimary ¶
NewPrimary creates a Primary wrapper for an AWS client configured for the primary region. Use this in your client factory when registering with ForPrimaryRegion():
blwa.WithAWSClient(func(cfg aws.Config) *blwa.Primary[ssm.Client] {
return blwa.NewPrimary(ssm.NewFromConfig(cfg))
}, blwa.ForPrimaryRegion())
type Region ¶
type Region interface {
// contains filtered or unexported methods
}
Region represents a target AWS region for client creation.
func FixedRegion ¶
FixedRegion returns a Region that uses a specific region string.
func LocalRegion ¶
func LocalRegion() Region
LocalRegion returns a Region that uses the Lambda's AWS_REGION.
func PrimaryRegion ¶
func PrimaryRegion() Region
PrimaryRegion returns a Region that uses the PRIMARY_REGION env var. Use this for cross-region operations that must target the primary deployment region.
type Runtime ¶
type Runtime[E Environment] struct { // contains filtered or unexported fields }
Runtime provides access to app-scoped dependencies. Inject this into handler constructors via fx instead of pulling from context.
Example:
type Handlers struct {
rt *blwa.Runtime[Env]
dynamo *dynamodb.Client
}
func NewHandlers(rt *blwa.Runtime[Env], dynamo *dynamodb.Client) *Handlers {
return &Handlers{rt: rt, dynamo: dynamo}
}
func (h *Handlers) GetItem(ctx context.Context, w bhttp.ResponseWriter, r *http.Request) error {
env := h.rt.Env()
url, _ := h.rt.Reverse("get-item", id)
h.dynamo.GetItem(ctx, ...)
// ...
}
func NewRuntime ¶
func NewRuntime[E Environment](env E, mux *Mux, params RuntimeParams) *Runtime[E]
NewRuntime creates a new Runtime with the given dependencies.
func (*Runtime[E]) Reverse ¶
Reverse returns the URL for a named route with the given parameters. The route must have been registered with a name using Handle/HandleFunc.
func (*Runtime[E]) Secret ¶
func (r *Runtime[E]) Secret(ctx context.Context, secretID string, jsonPath ...string) (string, error)
Secret retrieves a secret value from AWS Secrets Manager.
The secretID is the secret name or ARN to read from (required). If jsonPath is provided, the secret is parsed as JSON and the path is extracted using gjson syntax (e.g., "database.password", "api.keys.0"). If jsonPath is omitted, the raw secret string is returned.
Secrets are cached but fetched per-request to support rotation without redeployment.
Example:
// Raw string secret apiKey, err := h.rt.Secret(ctx, "my-api-key-secret") // JSON secret with path extraction password, err := h.rt.Secret(ctx, "my-db-credentials", "password")
type RuntimeParams ¶
type RuntimeParams struct {
SecretReader SecretReader
}
RuntimeParams holds optional dependencies for Runtime.
type SecretReader ¶
type SecretReader interface {
GetSecretString(ctx context.Context, secretID string) (string, error)
}
SecretReader abstracts secret retrieval for testability and flexibility.
type ServerConfig ¶
type ServerConfig struct {
HealthHandler func(http.ResponseWriter, *http.Request)
}
ServerConfig holds optional configuration for the HTTP server.
type ServerParams ¶
type ServerParams struct {
fx.In
Env Environment
Mux *Mux
Logger *zap.Logger
TracerProv trace.TracerProvider
Propagator propagation.TextMapPropagator
}
ServerParams holds the dependencies for creating an HTTP server.