End-to-end sample wiring up:
client → Lambda(producer, Hono) → SQS(hello-queue) → Lambda(consumer) → CloudWatch Logs
…all running locally inside Floci — the free, open-source AWS local emulator. Workspaces managed by pnpm and orchestrated by Turborepo.
- pnpm workspaces + Turborepo task pipeline
- Hono v4 with
hono/aws-lambdaadapter (producer) - @aws-sdk/client-sqs v3 (producer → SQS)
- esbuild bundles each Lambda to a single CJS file
- Floci runs Lambdas in real Docker containers (
public.ecr.aws/lambda/nodejs:22) and auto-injectsAWS_ENDPOINT_URL=http://floci:4566so the producer can reach SQS on the sharedfloci-netDocker network - AWS CLI v2 drives setup/deploy/log-tail from the host
.
├── apps/
│ ├── producer/ # Hono Lambda — GET /hello/:name → SendMessage to SQS
│ └── consumer/ # SQS-triggered Lambda — logs structured JSON
├── packages/
│ └── shared/ # Shared types + queue name constant
├── scripts/
│ ├── env.sh # AWS endpoint + creds + names
│ ├── setup-queue.sh # create SQS queue
│ ├── deploy.sh # deploy both Lambdas + event source mapping + Function URL
│ ├── invoke-producer.sh
│ └── tail-logs.sh # read /aws/lambda/consumer
├── docker-compose.yml # floci service on floci-net (FLOCI_HOSTNAME=floci)
├── pnpm-workspace.yaml
├── turbo.json
└── package.json
- Node.js 20+
- pnpm 10+ (
corepack enable && corepack prepare pnpm@latest --activate) - Docker (Docker Desktop or OrbStack)
- AWS CLI v2 (
brew install awscli)
# 1) Install dependencies
pnpm install
# 2) Start Floci
pnpm floci:up
# 3) Create the SQS queue
pnpm run setup # NOTE: `run` is required — `pnpm setup` is reserved
# 4) Build, package, deploy both Lambdas, wire SQS trigger + Function URL
pnpm run deploy
# 5) Trigger producer (sends a message to SQS)
pnpm run invoke alice
# 6) Inspect consumer's CloudWatch logs
pnpm run logsStop everything:
pnpm floci:downFloci issues a real Lambda Function URL like
http://<id>.lambda-url.us-east-1.floci:4566/ — that hostname only resolves
inside the floci-net Docker network. From the host, route via localhost:4566
plus a Host: header:
# Auto-resolve the URL ID, then curl through it
HOST=$(AWS_ENDPOINT_URL=http://localhost:4566 AWS_DEFAULT_REGION=us-east-1 \
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test \
aws lambda get-function-url-config --function-name producer \
--query 'FunctionUrl' --output text | sed -E 's|http://([^/]+)/.*|\1|; s|:4566$||')
# health
curl -sS -H "Host: $HOST" http://localhost:4566/
# enqueue a message
curl -sS -H "Host: $HOST" http://localhost:4566/hello/alice
# → {"enqueued":{"greeting":"Hello, alice!", ...},"messageId":"..."}
# verify the consumer processed it
aws --endpoint-url=http://localhost:4566 logs tail /aws/lambda/consumer --since 5mproducerHono app receivesGET /hello/:name, builds aHelloMessage, andSendMessages to SQS athttp://floci:4566/000000000000/hello-queue(the in-container endpoint).- Floci's event source mapping (created by
deploy.sh) forwards new SQS messages to theconsumerLambda. consumerparses the body andconsole.logs a structured line — Floci writes that to/aws/lambda/consumerin its CloudWatch emulator.tail-logs.sh(oraws logs tail) reads the latest stream.
| Method | Path | Behaviour |
|---|---|---|
| GET | / |
{ ok, service, queueUrl, endpoint } |
| GET | /hello/:name |
enqueues HelloMessage → { enqueued, messageId } |
pnpm setupis reserved — always usepnpm run setup. Same can apply to other reserved names; preferpnpm run <script>for project scripts.- Function URL hostname: the URL Floci returns is only reachable inside its Docker network; from the host, use the
Host:header trick shown above. @app/sharedresolution: producer and consumer reference it viaworkspace:*; esbuild bundles it directly — no separate build step.- Consumer uses partial-batch reporting (
SQSBatchResponse.batchItemFailures) so a single bad message doesn't reprocess the whole batch. - No real AWS resources are created — everything stays inside the Floci container.