Documentation
Feedback
Guides
App Development

App Development
ServicesDeveloping services on VTEX IO
2. Handling and receiving events

Some interactions on VTEX IO can generate events and be used as triggers for actions. For instance, an app listening for order placement events can respond by sending a confirmation email. In this course, we'll explore the process of firing events using the events-example app and then demonstrate how the service-course-template app can effectively listen to and handle these events.

Events are workspace and account-bound, meaning they are only visible for the account and workspace where they were fired. Events fired on your personal workspace will only be listened to by apps linked to this same workspace.

Logging into your account

To begin, log in to your VTEX account and create a devolopment workspace using the terminal. Replace {accountName} with your VTEX account name.


_10
vtex login {accountName}

Cloning the app responsible for firing events

Now, let's clone the events-example app, responsible for firing the events that the service-course-template will listen to.


_10
git clone https://github.com/vtex-apps/events-example

Linking the app to a development workspace

Change to the events-example directory and link the app to your development workspace. After running vtex link on the events-example app, the terminal should prompt a healthcheck route. Access the healthcheck route using your browser. You should see an ok text.

Accessing the healthcheck route creates a cache context needed for the VTEX IO to fire events. Without it, the events-example app won't be able to fire the events our app is going to listen to.


_10
cd events-example && vtex link

Terminal

_40
12:06:38.229 - info: Running yarn in node
_40
yarn install v1.22.19
_40
warning package.json: No license field
_40
warning No license field
_40
[1/4] 🔍 Resolving packages...
_40
[2/4] 🚚 Fetching packages...
_40
[3/4] 🔗 Linking dependencies...
_40
[4/4] 🔨 Building fresh packages...
_40
success Saved lockfile.
_40
✨ Done in 8.30s.
_40
12:06:47.012 - info: Finished running yarn
_40
12:06:47.013 - info: Linking app vtex.events-example@0.0.1
_40
12:06:47.116 - info: Sending 16 files
_40
12:06:47.118 - info: Link ID: 089d3cc8f38f0dfc
_40
12:06:47.118 - info: Project size: 0.26MB
_40
12:06:47.119 - info: Compressing project files...
_40
12:06:47.166 - info: Compressed project size: 0.11MB
_40
12:06:48.929 - info: Sending files: 100% - 0.11MB/0.11MB
_40
12:06:49.174 - info: Build accepted for vtex.events-example@0.0.1 at myaccout/myworkspace vtex.builder-hub@0.296.0
_40
12:06:49.175 - info: Starting build for app vtex.events-example@0.0.1 vtex.builder-hub@0.296.0
_40
12:06:54.769 - info: Bundle: 18 files - 0.33MB vtex.builder-hub@0.296.0
_40
12:06:54.789 - info: Sent bundle to Apps in 826ms vtex.builder-hub@0.296.0
_40
12:06:54.807 - info: App linked successfully vtex.builder-hub@0.296.0
_40
12:06:54.856 - info: Node app build finished successfully vtex.builder-hub@0.296.0
_40
12:06:58.871 - info: Starting vtex.events-example@0.0.1 service-node@6.38.1
_40
12:06:58.886 - info: Fetching /apps/vtex.events-example@0.0.1/bundle/dist/service-node/app service-node@6.38.1
_40
12:06:58.887 - info: Fetching /apps/vtex.events-example@0.0.1/files/dist/service-node/dependencies.tar.zst service-node@6.38.1
_40
12:06:58.986 - info: OK: /apps/vtex.events-example@0.0.1/files/dist/service-node/dependencies.tar.zst service-node@6.38.1
_40
12:06:59.000 - info: Extracted /apps/vtex.events-example@0.0.1/files/dist/service-node/dependencies.tar.zst service-node@6.38.1
_40
12:06:59.005 - info: OK: /apps/vtex.events-example@0.0.1/bundle/dist/service-node/app service-node@6.38.1
_40
12:06:59.012 - info: Extracted /apps/vtex.events-example@0.0.1/bundle/dist/service-node/app service-node@6.38.1
_40
12:06:59.621 - info: Runtime @vtex/api is: /usr/local/app/node_modules/@vtex/api/lib/index.js service-node@6.38.1
_40
12:06:59.631 - info: Using @vtex/api from: /usr/local/app/node_modules/@vtex/api/lib/index.js service-node@6.38.1
_40
12:06:59.639 - info: Spawning 1 workers
_40
Using 30 seconds as worker graceful shutdown timeout service-node@6.38.1
_40
12:06:59.665 - info: Worker 26 is listening service-node@6.38.1
_40
12:07:00.328 - info: FIRED HERE service-node@6.38.1
_40
12:07:00.333 - info: Available service routes:
_40
https://myworkspace--myaccout.myvtex.com/_v/app/events-example/hcheck service-node@6.38.1
_40
12:07:00.341 - info: App running service-node@6.38.1

Handling events

Now, let's return to the service-course-template app to manage events triggered by the events-example app effectively. This involves the following steps:

  • Set up the service-course-template app to listen to events emitted by the events-example app.
  • Create an event handler function for processing the events received from the events-example app.
  • Configure the service-course-template app to use the handler function to handle the listened events.

Listening to events

In order to listen to the events sent by the events-example app, open the service.json file and declare the highlighted code to give the app's service this capability. Note that we declare this by using the events resolver, the reference of the app that fires the event (sender), and the event reference key (keys).

node/service.json

_22
{
_22
"memory": 128,
_22
"ttl": 10,
_22
"timeout": 10,
_22
"minReplicas": 2,
_22
"maxReplicas": 10,
_22
"workers": 4,
_22
"events": {
_22
"liveUsersUpdate": {
_22
"sender": "vtex.events-example",
_22
"keys": [
_22
"send-event"
_22
]
_22
}
_22
},
_22
"routes": {
_22
"analytics": {
_22
"path": "/_v/app/analytics/realTime",
_22
"public": true
_22
}
_22
}
_22
}

Creating an event handler function

Once we are able to listen to the events sent by the events-example app, let's create a function to handle these events. For now, let's create a log when receiving an event. To do so, open the /node/event directory and update the liveUsersUpdate.ts file with the code presented.

node/service.json
node/event/liveUsersUpdate.ts

_10
export async function updateLiveUsers() {
_10
console.log('EVENT HANDLER: received event')
_10
}

Defining the event handler

Now, we need to specify how the event handler function will be used. Open the service-course-template/node/index.ts file and update the Service declaration with the highlighted lines.

node/service.json
node/event/liveUsersUpdate.ts
node/index.ts

_55
import {
_55
LRUCache,
_55
Service,
_55
ServiceContext,
_55
ParamsContext,
_55
RecorderState,
_55
method,
_55
} from '@vtex/api'
_55
import { Clients } from './clients'
_55
import { analytics } from './handlers/analytics'
_55
import { updateLiveUsers } from './event/liveUsersUpdate'
_55
_55
// Create a LRU memory cache for the Status client.
_55
// The @vtex/api HttpClient respects Cache-Control headers and uses the provided cache.
_55
const memoryCache = new LRUCache<string, any>({ max: 5000 })
_55
metrics.trackCache('status', memoryCache)
_55
_55
declare global {
_55
type Context = ServiceContext<Clients, State>
_55
_55
interface State extends RecorderState {
_55
code: number
_55
}
_55
}
_55
_55
const THREE_SECONDS_MS = 3 * 1000
_55
const CONCURRENCY = 10
_55
_55
export default new Service<Clients, State, ParamsContext>({
_55
clients: {
_55
implementation: Clients,
_55
options: {
_55
default: {
_55
retries: 2,
_55
timeout: 10000,
_55
},
_55
events: {
_55
exponentialTimeoutCoefficient: 2,
_55
exponentialBackoffCoefficient: 2,
_55
initialBackoffDelay: 50,
_55
retries: 1,
_55
timeout: THREE_SECONDS_MS,
_55
concurrency: CONCURRENCY,
_55
},
_55
},
_55
},
_55
events: {
_55
liveUsersUpdate: updateLiveUsers,
_55
},
_55
routes: {
_55
analytics: method({
_55
GET: [analytics],
_55
}),
_55
},
_55
})

Configuring the event handler behavior

We can now specify how the event handler will behave. In the service-course-template/node/index.ts file, update the Service declaration with the highlighted lines. Going by each configuration, we have the following:

FieldTypeDescription
exponentialTimeoutCoefficientIntegerExponential factor by which the timeout will increase in each retry, in seconds.
exponentialBackoffCoefficientIntegerExponential factor by which the backoff delay will increase in each retry, in seconds.
initialBackoffDelayIntegerTime, in seconds, that the app will wait until the next retry.
retriesIntegerMaximum times the app will retry.
timeoutIntegerTimeout until the attempt is considered a failure, in seconds.
concurrencyIntegerAmount of simultaneous processes the event is able to perform.

By adding this code to the Service, we are adding to the Client of this Service, the capability to handle events. At this point, we are not yet using the Client itself when handling the event.

node/service.json
node/event/liveUsersUpdate.ts
node/index.ts

_55
import {
_55
LRUCache,
_55
Service,
_55
ServiceContext,
_55
ParamsContext,
_55
RecorderState,
_55
method,
_55
} from '@vtex/api'
_55
import { Clients } from './clients'
_55
import { analytics } from './handlers/analytics'
_55
import { updateLiveUsers } from './event/liveUsersUpdate'
_55
_55
// Create a LRU memory cache for the Status client.
_55
// The @vtex/api HttpClient respects Cache-Control headers and uses the provided cache.
_55
const memoryCache = new LRUCache<string, any>({ max: 5000 })
_55
metrics.trackCache('status', memoryCache)
_55
_55
declare global {
_55
type Context = ServiceContext<Clients, State>
_55
_55
interface State extends RecorderState {
_55
code: number
_55
}
_55
}
_55
_55
const THREE_SECONDS_MS = 3 * 1000
_55
const CONCURRENCY = 10
_55
_55
export default new Service<Clients, State, ParamsContext>({
_55
clients: {
_55
implementation: Clients,
_55
options: {
_55
default: {
_55
retries: 2,
_55
timeout: 10000,
_55
},
_55
events: {
_55
exponentialTimeoutCoefficient: 2,
_55
exponentialBackoffCoefficient: 2,
_55
initialBackoffDelay: 50,
_55
retries: 1,
_55
timeout: THREE_SECONDS_MS,
_55
concurrency: CONCURRENCY,
_55
},
_55
},
_55
},
_55
events: {
_55
liveUsersUpdate: updateLiveUsers,
_55
},
_55
routes: {
_55
analytics: method({
_55
GET: [analytics],
_55
}),
_55
},
_55
})

Linking the event handler app

In the service-course-template directory, run vtex link and wait for the event to be fired by the events-example app. Upon successful reception, a log entry should appear in the console.

node/service.json
node/event/liveUsersUpdate.ts
node/index.ts
Terminal

_23
12:58:15.238 - info: Running yarn in node
_23
yarn install v1.22.19
_23
warning package.json: No license field
_23
warning No license field
_23
[1/4] 🔍 Resolving packages...
_23
[2/4] 🚚 Fetching packages...
_23
[3/4] 🔗 Linking dependencies...
_23
[4/4] 🔨 Building fresh packages...
_23
_23
success Saved lockfile.
_23
✨ Done in 20.08s.
_23
12:58:35.636 - info: Finished running yarn
_23
12:58:35.636 - info: Linking app vtex.backend-course@0.0.2
_23
12:58:35.715 - info: Sending 13 files
_23
12:58:35.717 - info: Link ID: 0d190e115e9b2d6b
_23
12:58:35.717 - info: Project size: 0.12MB
_23
12:58:35.717 - info: Compressing project files...
_23
12:58:35.761 - info: Compressed project size: 0.05MB
_23
12:58:37.251 - info: Sending files: 100% - 0.05MB/0.05MB
_23
12:58:37.252 - info: Build accepted for vtex.backend-course@0.0.2 at myaccout/myworkspace vtex.builder-hub@0.296.0
_23
12:58:37.253 - info: Starting build for app vtex.backend-course@0.0.2 vtex.builder-hub@0.296.0
_23
12:58:40.140 - info: EVENT HANDLER: received event service-node@6.38.1
_23
12:58:40.141 - info: [15:58:40.429Z] [26] myaccout/myworkspace:liveUsersUpdate 204 POST /myaccout/myworkspace/_events 0 ms service-node@6.38.1

Logging into your account

To begin, log in to your VTEX account and create a devolopment workspace using the terminal. Replace {accountName} with your VTEX account name.

Cloning the app responsible for firing events

Now, let's clone the events-example app, responsible for firing the events that the service-course-template will listen to.

Linking the app to a development workspace

Change to the events-example directory and link the app to your development workspace. After running vtex link on the events-example app, the terminal should prompt a healthcheck route. Access the healthcheck route using your browser. You should see an ok text.

Accessing the healthcheck route creates a cache context needed for the VTEX IO to fire events. Without it, the events-example app won't be able to fire the events our app is going to listen to.

Handling events

Now, let's return to the service-course-template app to manage events triggered by the events-example app effectively. This involves the following steps:

  • Set up the service-course-template app to listen to events emitted by the events-example app.
  • Create an event handler function for processing the events received from the events-example app.
  • Configure the service-course-template app to use the handler function to handle the listened events.

Listening to events

In order to listen to the events sent by the events-example app, open the service.json file and declare the highlighted code to give the app's service this capability. Note that we declare this by using the events resolver, the reference of the app that fires the event (sender), and the event reference key (keys).

Creating an event handler function

Once we are able to listen to the events sent by the events-example app, let's create a function to handle these events. For now, let's create a log when receiving an event. To do so, open the /node/event directory and update the liveUsersUpdate.ts file with the code presented.

Defining the event handler

Now, we need to specify how the event handler function will be used. Open the service-course-template/node/index.ts file and update the Service declaration with the highlighted lines.

Configuring the event handler behavior

We can now specify how the event handler will behave. In the service-course-template/node/index.ts file, update the Service declaration with the highlighted lines. Going by each configuration, we have the following:

FieldTypeDescription
exponentialTimeoutCoefficientIntegerExponential factor by which the timeout will increase in each retry, in seconds.
exponentialBackoffCoefficientIntegerExponential factor by which the backoff delay will increase in each retry, in seconds.
initialBackoffDelayIntegerTime, in seconds, that the app will wait until the next retry.
retriesIntegerMaximum times the app will retry.
timeoutIntegerTimeout until the attempt is considered a failure, in seconds.
concurrencyIntegerAmount of simultaneous processes the event is able to perform.

By adding this code to the Service, we are adding to the Client of this Service, the capability to handle events. At this point, we are not yet using the Client itself when handling the event.

Linking the event handler app

In the service-course-template directory, run vtex link and wait for the event to be fired by the events-example app. Upon successful reception, a log entry should appear in the console.


_10
vtex login {accountName}

Contributors
1
Photo of the contributor
+ 1 contributors
Was this helpful?
Yes
No
Suggest edits (Github)
Contributors
1
Photo of the contributor
+ 1 contributors
On this page