| Key | Value |
|---|---|
| Environment | LocalStack, AWS |
| Services | Lambda (durable functions), API Gateway, DynamoDB, EventBridge, AppSync Events, S3, CloudFormation |
| Integrations | AWS SAM, AWS CLI, lstk, Jest, Playwright |
| Categories | Serverless, Event-Driven Architecture |
| Level | Intermediate |
| Use Case | Lambda Durable Functions, Integration Testing, Resource Browser, App Inspector, Persistence |
| GitHub | Repository link |
This sample runs Serverlesspresso, a coffee ordering app built on AWS Lambda durable functions, on LocalStack. An attendee orders a coffee, a barista accepts it and then completes it, and both screens update in real time.
The whole order workflow is one durable function. It writes the order, checks two things in parallel, queues the order and then waits for the barista with waitForCallback. While it waits, nothing runs. When the barista acts, a callback resumes the execution, and the function replays from the top and skips the steps that already finished.
The app is a fork of singledigit/durable-serverlesspresso by Eric Johnson. This fork adds:
- A
Makefileand a projectlstkconfig, so the app deploys to LocalStack withmake deploy. - A test suite that runs against LocalStack: 15 Jest integration tests and a Playwright test that drives the UI. The original unit tests, which use the durable execution SDK's local test runner, still run as they are.
- Two template parameters that make the barista timeouts configurable, so the tests can run the timeout paths in seconds instead of minutes. The defaults keep the original two minutes.
The same template deploys to AWS without changes.
- Lambda runs the
CoffeeOrdersdurable function (the order workflow), the callback handler that resumes it, a function that publishes status events, and a function that returns an execution's history to the UI. - API Gateway starts an execution for each new order, puts barista actions on EventBridge, and reads orders straight from DynamoDB.
- DynamoDB stores the orders, including the callback ID the workflow is waiting on, and the event configuration.
- EventBridge routes barista actions and cancel requests to the callback handler, which reads the callback ID from the order and calls
SendDurableExecutionCallbackSuccess. A second rule sends status events to the publisher function. - AppSync Events pushes every status change to the attendee and barista screens over a WebSocket.
- S3 hosts the Vue frontend as a static website.
- CloudFormation deploys everything from one SAM template.
- A LocalStack account with a paid plan or a trial. Durable executions are not available on the free Hobby plan.
lstk, the LocalStack CLI:brew install localstack/tap/lstkornpm install -g @localstack/lstk- Docker
- AWS SAM CLI 1.166 or later. Older versions reject the
DurableConfigproperty. - AWS CLI, recent enough to have the durable execution commands (check with
aws lambda get-durable-execution-history help) - Node.js 22 and esbuild on your
PATHforsam build:npm install -g esbuild make
Clone the repository:
git clone https://github.com/localstack-samples/durable-serverlesspresso.git
cd durable-serverlesspressoInstall the frontend and test dependencies:
make installRun make on its own to list all targets. Every target prints the commands it runs.
Start LocalStack from the repository root. lstk reads .lstk/config.toml, which uses the dev image and turns on App Inspector:
make startBuild and deploy the stack, then open the coffee shop:
make deployThis runs lstk sam build and lstk sam deploy, the regular SAM CLI pointed at LocalStack, and then seeds the event configuration (store open, three orders per attendee). On a laptop the deploy takes about a minute.
Build the frontend and host it on an S3 website:
make frontendThe output ends with the two URLs:
Attendee: http://durable-serverlesspresso-frontend.s3-website.localhost.localstack.cloud:4566/attendee
Barista: http://durable-serverlesspresso-frontend.s3-website.localhost.localstack.cloud:4566/baristaOpen the barista URL in one browser window and the attendee URL in an incognito window. The app keeps its state in localStorage, so the two roles need separate browser profiles.
- As the attendee, pick a drink and a size and place the order. The order appears on the barista dashboard.
- As the barista, click Accept, then Complete. The attendee sees the order move to preparing and then ready.
- On the barista dashboard, open History and View History on the order to see the durable execution's steps.
Each wait for the barista times out after two minutes, after which the workflow cancels the order.
Note
LocalStack's TLS certificate does not cover the AppSync Events realtime host yet, so a regular browser cannot open the realtime WebSocket. The app still works: reload the page to see status changes. The Playwright test ignores certificate errors and checks the live updates.
While an order waits for the barista, list the executions of the order workflow:
make executionsThe order shows as RUNNING, although no invocation is running. Show the event history of the latest execution:
make historyThe history lists every step that finished, the two parallel validation branches and, at the end, wait-acceptance with CallbackStarted and InvocationCompleted: the function stopped and waits for the callback. After the barista completes the order, the history shows three invocations and ends with ExecutionSucceeded.
| Target | What it runs |
|---|---|
make test-unit |
The original unit tests with the durable execution SDK's local test runner. They need no LocalStack. |
make test |
15 Jest integration tests against the deployed stack: placing orders, parallel validation, accept, complete and cancel through EventBridge and the callback handler, both timeouts, the REST API, execution history and the AppSync Events channels. |
make test-ui |
A Playwright test against the S3 website: the attendee orders, the barista accepts and completes, and both screens update over AppSync Events. |
make test-all |
Redeploys with 15 second timeouts and runs all of the above. |
With the default two minute timeouts, the two timeout tests skip themselves. make test-all deploys with TIMEOUTS=15 so they run. To deploy with short timeouts yourself, run make deploy TIMEOUTS=15.
.github/workflows/integration-test.yml runs on every push and pull request, and weekly. It runs the unit tests, starts LocalStack with lstk, deploys with make deploy TIMEOUTS=15 and runs make test. It needs a LOCALSTACK_AUTH_TOKEN secret.
The Resource Browser in the LocalStack web app shows the resources of the stack. Open Lambda for the four functions and the durable function's logs, and DynamoDB for the orders and the callback IDs the workflow stored. If Chrome asks to allow access to your local network, allow it so the web app can reach LocalStack.
App Inspector traces the calls between services. Search for Invoke and open View Graph on the API Gateway row: it shows API Gateway invoking the durable function, the function writing to DynamoDB, and a CheckpointDurableExecution call for each step. .lstk/config.toml turns App Inspector on at startup.
With persistence on, a durable execution survives a restart of the emulator. Persistence is off by default, so each lstk start begins with an empty emulator. To try it, uncomment PERSISTENCE = "1" in .lstk/config.toml, restart LocalStack (make stop start), deploy with the default timeouts, and run:
make restart-demoThe script places an order, accepts it, restarts LocalStack while the workflow waits for the barista, completes the order and shows that initialize-order ran only once.
- Every order fails and the logs say
Cannot find module 'index'. The stack was deployed without a build, so the functions contain the TypeScript sources instead of the esbuild bundle.sam deployuses.aws-sam/buildonly ifsam buildran first in the same folder. Runmake deploy, which always builds first. - The barista header says "Loading..." and the store toggle fails with
No event loaded. The event configuration is missing.make deployseeds it, or runscripts/seed-config.sh.
make destroy
make stop- Lambda durable functions on LocalStack: supported features and current limitations
- Testing Lambda Durable Functions Locally with LocalStack
- AWS Lambda durable functions
lstk- The original app and its walkthrough video
