Skip to main content

Event Driven Architecture

Overview

In this tutorial, we'll use Klotho config to demonstrate how to build an application with an event driven architecture, using Klotho's pubsub annotation.

Event Driven Architecture

Why?

Some applications only need actions to be taken on specific events. By using Klotho's pubsub capability, we can see how to trigger our application to only run when necessary, thus saving cost.

Getting started with event driven architectures

Prerequisites

Repository

Clone our sample apps git repo and install the npm packages for the ts-events app.

git clone https://github.com/klothoplatform/sample-apps.git
cd sample-apps/ts-events
npm install

Application Overview

The ts-events application utilizes the following annotations

/**
* @klotho::execution_unit {
* id = "events-api"
* }
*/

import * as express from "express";
...

/**
* @klotho::expose {
* target = "public"
* id = "app"
* }
*/
app.listen(3000, () => console.log("listening on :3000"))

Event Overview

By annotating an instance of EventEmitter, we can now create publishers and subscribers of our event driven architecture. We do so by declaring events to act on and events we will emit.

emitter.ts
/* @klotho::pubsub {
* id = "myEmmitter"
* }
*/
export const MyEmitter = new events.EventEmitter();

We define an event hello, which we will emit when our api endoint /hello/:user is called:

index.ts
...
app.post("/hello/:user", async (req, resp) => {
console.log("got", {params: req.params})
MyEmitter.emit('hello', req.params.user)
resp.status(201).send()
})
...

The hello event triggers our function within hello_handler.ts, which adds the user to a JavaScript Set, and proceeds to emit a other event:

hello_handler.ts
MyEmitter.on("hello", async (user) => {
console.log(`hello ${user}`);
await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep 1s
users.add(user);
console.log(`goodbye ${user}`);
MyEmitter.emit("other", "disconnecting");
});

The other event triggers our function within other/handler.ts:

other/handler.ts
pkg.MyEmitter.on("other", async () => {
console.log(`...`);
await new Promise((resolve) =>
setTimeout(resolve, Math.random() * 4000 + 1000)
); // sleep random 1-5 seconds
console.log(`!`);
});

Klotho compile the application

Let start by compiling our TypeScript application into JavaScript.

npx tsc

Then we'll compile using Klotho.

klotho              \
. \
--provider aws \
--app myfirstapp \
--outDir compiled
██╗  ██╗██╗      ██████╗ ████████╗██╗  ██╗ ██████╗
██║ ██╔╝██║ ██╔═══██╗╚══██╔══╝██║ ██║██╔═══██╗
█████╔╝ ██║ ██║ ██║ ██║ ███████║██║ ██║
██╔═██╗ ██║ ██║ ██║ ██║ ██╔══██║██║ ██║
██║ ██╗███████╗╚██████╔╝ ██║ ██║ ██║╚██████╔╝
╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝

Adding resource input_file_dependencies:
Adding resource exec_unit:events-api
Adding resource exec_unit:events-hello
Adding resource exec_unit:events-other
index.js:21:0: Found 1 route(s) on server 'app' var: app
Adding resource gateway:app
Adding resource pubsub:myEmmitter
index.js: Found 1 topics produced to emitter.js#MyEmitter: [hello]
hello_handler.js: Found 1 topics produced to emitter.js#MyEmitter: [other]
hello_handler.js: Found 1 topics consumed from emitter.js#MyEmitter: [hello]
other/handler.js: Found 1 topics consumed from emitter.js#MyEmitter: [other]
Adding resource topology:myfirstapp
Adding resource infra_as_code:Pulumi (AWS)
Pulumi.myfirstapp.yaml: Make sure to run `pulumi config set aws:region YOUR_REGION --cwd 'compiled/' -s 'myfirstapp'` to configure the target AWS region.

Deploying the application

To deploy the application, refer to the deploying guide.