AWS Step Function
AWS Step Function is a serverless orchestration service that lets developers create and manage multi-step application workflows. Use cases for Step Functions vary widely, from orchestrating serverless microservices, to building data-processing pipelines.
In this application we will use Step Function to orchestrate the Lambda functions in our vacation booking application.
Step Functions configuration
To get started, first install the serverless-step-functions
plugin. Run the following command.
$ npm i serverless-step-functions --save
Add, the step function dependency in your serverless.yml
file.
# partials of serverless.yml
service: fauna-aws-workshop
frameworkVersion: '2.72.3'
# Specify donenv
useDotenv: true
# Add Fauna plugin
plugins:
- serverless-dotenv-plugin
- "@fauna-labs/serverless-fauna"
- serverless-step-functions
#...rest of the file
Next, you will create a state machine to orchestrate the Lambdas. Add the following code snippet to serverless.yml
file.
# partials of serverless.yml
stepFunctions:
stateMachines:
bookVacationStepFunc:
events:
- http:
method: POST
path: /book-vacation
cors: true
definition:
Comment: "Booking Hotel Step"
StartAt: BookHotel
States:
BookHotel:
Type: Task
Resource:
Fn::GetAtt: [bookHotel, Arn]
Next: BookFlight
BookFlight:
Type: Task
Resource:
Fn::GetAtt: [bookFlight, Arn]
Catch:
- ErrorEquals: ["States.TaskFailed"]
Next: CancelHotel
End: true
CancelHotel:
Type: Task
Resource:
Fn::GetAtt: [cancelHotel, Arn]
End: true
View the entire serverles.yml
file here.
Run sls deploy
command to deploy the latest changes.
When the deployment is done head back to AWS console. Select Step Function > State machines > your-step-function-name > Edit.
Notice that a Step Function workflow has been generated. Your Step Function workflow should be as follows.
The deployment also generates an API endpoint. Make a post-call to your generated API endpoint to check everything is working.
Successful booking scenario
POST [https://pc4jk9bsil.execute-api.us-east-1.amazonaws.com/dev/book-vacation](https://www.notion.so/AWS-Step-Function-d5adcb5d2b23487bbe704f612c31d612)
// payload
{
"userId": "TEST-TEST-3",
"flightId": 123
}
Observe the Step Function execution in your AWS console. You will be able to notice something similar as follows.
Failed booking scenario
POST [https://pc4jk9bsil.execute-api.us-east-1.amazonaws.com/dev/book-vacation](https://www.notion.so/AWS-Step-Function-d5adcb5d2b23487bbe704f612c31d612)
// payload
{
"userId": "TEST-fail-2",
"flightId": -1
}
At this point you have your event driven architecture setup.
Listening to DB changes with Fauna Event Streams
To make this solution work end to end let’s go ahead and create a simple frontend. In the fronted you can also subscribe to any changes that happens in the database. Fauna’s Event Stream API gives you the ability to subscribe to database events in real time.
✍️ Learn more about Event Streaming here.
Create a new file called index.html
in the root of your application. Add the following code.
<html>
<h1>Example Frontend</h1>
<body>
<input type="text" id="userId">
<p>
<span class="scores"></span>
</p>
<button onclick="createNewVacation()">Book Vacation</button>
</body>
<script src="https://cdn.jsdelivr.net/npm/faunadb@latest/dist/faunadb-min.js"></script>
<script type="text/javascript">
const faunadb = window.faunadb
const q = faunadb.query
const client = new faunadb.Client({
secret: 'fnAxxxx',
domain: 'db.fauna.com', // Adjust if you are using Region Groups
})
function report(e) {
console.log(e)
}
async function createNewVacation() {
const userId = document.getElementById('userId').value;
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
const raw = JSON.stringify({
userId,
"flightId": "231233"
});
let requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
let stream;
// https://ecos7fl9ec.execute-api.us-east-1.amazonaws.com/dev/book-vacation
setTimeout(() => {
stream.close();
}, 100000);
try {
const response = await fetch("https://ic6e58n5gk.execute-api.us-east-1.amazonaws.com/dev/book-vacation", requestOptions);
setTimeout(() => {
console.log("starting stream");
client.query(
q.Get(
q.Match(q.Index('vacations_by_userId'), userId)
)
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
'Error: [%s] %s: %s',
err.name,
err.message,
err.errors()[0].description,
))
const startStream = () => {
if(stream) {
stream.close()
}
stream = client.stream.document(docRef)
.on('snapshot', snapshot => {
report(snapshot)
})
.on('version', version => {
report(version)
})
.on('error', error => {
console.log('Error:', error)
stream.close()
setTimeout(startStream, 1000)
})
.start()
}
}, 5000);
} catch (error) {
console.log(error);
}
}
// startStream()
</script>
</html>
⚠️ Caution: While doing Event Streaming make sure to close the subscription when not in use. If you keep the subscription open (i.e. Open browser tab with subscribed stream) you will be charged for the usage.
In this next section you learn how to add event sourcing to this architecture.