A Go-based AWS Lambda for fetching configurations from AWS AppConfig using a cache-aside strategy.
For detailed business and technical architecture information, see:
- Business Documentation (Cost Impact and Savings)
- Architecture Documentation (3-Tier Cache and Technical Flow)
cmd/lambda: AWS Lambda entrypoint (API Gateway contract)cmd/local: Local runner for manual testingcmd/server: Local HTTP server for testing/integrationinternal/domain: Domain objectsinternal/application: Use cases and portsinternal/infrastructure: Adapters for AWS/Valkeyinternal/bootstrap: Dependency injection/composition
Use the .env file as a template:
AWS_REGIONVALKEY_HOST(optional; takes precedence if set)VALKEY_PORT(optional; defaults to6379when missing/empty)CACHE_SECRET_NAMEL1_TTL_SECONDSL2_TTL_SECONDSX_API_KEY(protects the/v1/configendpoint incmd/server)CIRCUIT_BREAKER_TABLE_NAME(optional; enables shared circuit breaker in DynamoDB)
If
VALKEY_HOSTis defined, the service uses environment variables for Valkey (with a defaultVALKEY_PORTof6379) and will not query the Secrets Manager.
When X_API_KEY is defined, authentication accepts:
x-api-tokenheaderx-api-tokenquery string parameter
{
"host": "my-valkey.xxxxxx.use1.cache.amazonaws.com",
"port": 6379
}Prerequisite: Valid AWS credentials configured in your environment.
If running on a machine or instance with an IAM Role (e.g., EC2), run with an SSO profile to avoid falling back to the instance role:
aws sso login --profile <your_sso_profile>
AWS_PROFILE=<your_sso_profile> make run-local APP=<app_id> ENV=<env_id> PROFILE=<profile_id>make fmt
make test
make run-local APP=<app_id> ENV=<env_id> PROFILE=<profile_id>
make run-server ADDR=:8080Start the server:
make run-server ADDR=:8080Healthcheck:
curl http://localhost:8080/healthGet configuration (GET):
curl "http://localhost:8080/v1/config?application=<app_id>&environment=<env_id>&profile=<profile_id>"
# alternative via query string API key:
curl "http://localhost:8080/v1/config?application=<app_id>&environment=<env_id>&profile=<profile_id>&x-api-token=<x_api_key>"Get configuration (POST):
curl -X POST http://localhost:8080/v1/config \
-H "Content-Type: application/json" \
-H "x-api-token: <x_api_key>" \
-d '{"application":"<app_id>","environment":"<env_id>","profile":"<profile_id>"}'The handler accepts application, environment, and profile in:
- Query string parameters (
?application=...&environment=...&profile=...) - Path parameters (
{application},{environment}, and{profile}) - JSON Body (
{"application":"...","environment":"...","profile":"..."})
If X_API_KEY is configured on the Lambda, you must also send:
x-api-tokenheader orx-api-tokenquery string parameter
The response is always JSON:
- 200 OK
{
"example_feature_flag": {
"enabled": true
}
}On success, the service returns the AppConfig document directly (without a wrapping
configurationobject).
- 400/500 Error
{
"message": "..."
}To enable the shared circuit breaker based on DynamoDB, add the following to your .env file:
CIRCUIT_BREAKER_TABLE_NAME=appconfig-circuit-breakerFor operation details, a guide on creating the DynamoDB table, and CLI monitoring commands, see the Circuit Breaker Section in the Architecture Documentation.
The project includes a multi-target Dockerfile:
server-runtime: A lightweight image to runcmd/serveras a non-root user.k6-runner: An image containing load testing scripts underscripts/k6.
docker build --target server-runtime -t appconfig-cache:server .docker run --rm \
--name appconfig-cache \
--cpus="0.50" \
--memory="256m" \
-p 8080:8080 \
--env-file .env \
appconfig-cache:serverdocker build --target k6-runner -t appconfig-cache:k6 .scripts/k6/cpu_bound_test.js forces CPU load on the load generator (repeated hashing), which is useful to validate behavior when k6 itself becomes the bottleneck.
docker run --rm \
--cpus="0.50" \
--memory="256m" \
appconfig-cache:k6 run /k6/scripts/cpu_bound_test.js \
-e VUS=2 \
-e DURATION=45s \
-e ROUNDS_PER_ITERATION=15000scripts/k6/io_bound_test.js prioritizes network/IO wait, making HTTP requests to /v1/config.
docker run --rm \
--cpus="0.50" \
--memory="256m" \
--add-host=host.docker.internal:host-gateway \
appconfig-cache:k6 run /k6/scripts/io_bound_test.js \
-e TARGET_URL="http://host.docker.internal:8080/v1/config?application=<app_id>&environment=<env_id>&profile=<profile_id>" \
-e X_API_KEY="<optional_x_api_key>" \
-e RATE=20 \
-e PARALLEL_REQUESTS=4Tip: If your goal is to stress the application (not k6), keep the
cpu_bound_test'sROUNDS_PER_ITERATIONlow and prioritizeio_bound_test.
make build-lambdaGenerated artifact:
-
dist/lambda.zip -
Current build architecture:
amd64
For guidelines on local environment setup, Git hooks installation, and development practices, please check our Contributing Guide.