2024-07-07 15:03:10 +08:00
# Event Emitter middleware for Hono
2025-03-19 16:53:11 +08:00
[](https://codecov.io/github/honojs/middleware)
2024-08-06 20:05:35 +08:00
### Minimal, lightweight and edge compatible Event Emitter middleware for [Hono](https://github.com/honojs/hono).
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
## Table of Contents
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
1. [Introduction ](#introduction )
2. [Installation ](#installation )
3. [Usage Examples ](#usage-examples )
2025-03-19 16:53:11 +08:00
- [1. As Hono middleware ](#1-as-hono-middleware )
- [2. Standalone ](#2-standalone )
2024-08-06 20:05:35 +08:00
4. [API Reference ](#api-reference )
2025-03-19 16:53:11 +08:00
- [emitter ](#emitter )
- [createEmitter ](#createemitter )
- [defineHandler ](#definehandler )
- [defineHandlers ](#definehandlers )
- [Emitter API Documentation ](#emitter )
2024-08-06 20:05:35 +08:00
5. [Types ](#types )
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
## Introduction
This library provides an event emitter middleware for Hono, allowing you to easily implement and manage event-driven architectures in your Hono applications.
It enables event driven logic flow, allowing you to decouple your code and make it more modular and maintainable.
2024-07-07 15:03:10 +08:00
Inspired by event emitter concept in other frameworks such
2024-08-06 20:05:35 +08:00
as [Adonis.js ](https://docs.adonisjs.com/guides/emitter ), [Nest.js ](https://docs.nestjs.com/techniques/events ), [Hapi.js ](https://github.com/hapijs/podium ), [Meteor ](https://github.com/Meteor-Community-Packages/Meteor-EventEmitter ) and others.
See [FAQ ](#faq ) bellow for some common questions.
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
For more usage examples, see the [tests ](src/index.test.ts ) or my [Hono REST API starter kit ](https://github.com/DavidHavl/hono-rest-api-starter )
2024-07-07 15:03:10 +08:00
## Installation
```sh
npm install @hono/event -emitter
# or
yarn add @hono/event -emitter
# or
pnpm add @hono/event -emitter
# or
bun install @hono/event -emitter
```
## Usage
#### There are 2 ways you can use this with Hono:
### 1. As Hono middleware
```js
// event-handlers.js
// Define event handlers
export const handlers = {
'user:created': [
2025-03-19 16:53:11 +08:00
(c, payload) => {}, // c is current Context, payload is whatever the emit method passes
2024-07-07 15:03:10 +08:00
],
'user:deleted': [
2025-03-19 16:53:11 +08:00
async (c, payload) => {}, // c is current Context, payload is whatever the emit method passes
2024-07-07 15:03:10 +08:00
],
2025-03-19 16:53:11 +08:00
foo: [
(c, payload) => {}, // c is current Context, payload is whatever the emit method passes
2024-08-06 20:05:35 +08:00
],
2024-07-07 15:03:10 +08:00
}
// You can also define single event handler as named function
2024-08-06 20:05:35 +08:00
// export const fooHandler = (c, payload) => {
// // c is current Context, payload is whatever the emit method passes
2024-07-07 15:03:10 +08:00
// // ...
2024-08-06 20:05:35 +08:00
// console.log('New foo created:', payload)
2024-07-07 15:03:10 +08:00
// }
```
```js
// app.js
import { emitter } from '@hono/event-emitter'
2024-08-06 20:05:35 +08:00
import { handlers, fooHandler } from './event-handlers'
2024-07-07 15:03:10 +08:00
import { Hono } from 'hono'
// Initialize the app with emitter type
const app = new Hono()
// Register the emitter middleware and provide it with the handlers
2024-08-06 20:05:35 +08:00
app.use(emitter(handlers))
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
// You can also add event listener inside middleware or route handler, but please only use named functions to prevent duplicates and memory leaks!
2024-07-07 15:03:10 +08:00
// app.use((c, next) => {
2024-08-06 20:05:35 +08:00
// c.get('emitter').on('foo', fooHandler)
2024-07-07 15:03:10 +08:00
// return next()
// })
// Routes
2024-08-06 20:05:35 +08:00
app.post('/users', (c) => {
2024-07-07 15:03:10 +08:00
// ...
// Emit event and pass current context plus the payload
2024-08-06 20:05:35 +08:00
c.get('emitter').emit(c, 'user:created', user)
2024-07-07 15:03:10 +08:00
// ...
})
2024-08-06 20:05:35 +08:00
app.delete('/users/:id', async (c) => {
2024-07-07 15:03:10 +08:00
// ...
2024-08-06 20:05:35 +08:00
// Emit event asynchronpusly and pass current context plus the payload
await c.get('emitter').emitAsync(c, 'user:deleted', id)
2024-07-07 15:03:10 +08:00
// ...
})
export default app
```
2024-08-06 20:05:35 +08:00
The emitter is available in the context as `emitter` key.
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
As seen above (commented out) you can also subscribe to events inside middlewares or route handlers,
but because middlewares are called on every request, you can only use named functions to prevent duplicates or memory leaks!
2024-07-07 15:03:10 +08:00
### 2 Standalone
```js
// events.js
import { createEmitter } from '@hono/event-emitter'
// Define event handlers
export const handlers = {
'user:created': [
2025-03-19 16:53:11 +08:00
(c, payload) => {}, // c is current Context, payload will be whatever you pass to emit method
2024-07-07 15:03:10 +08:00
],
'user:deleted': [
2025-03-19 16:53:11 +08:00
async (c, payload) => {}, // c is current Context, payload will be whatever you pass to emit method
],
2024-07-07 15:03:10 +08:00
}
// Initialize emitter with handlers
2024-08-06 20:05:35 +08:00
const ee = createEmitter(handlers)
2024-07-07 15:03:10 +08:00
// And you can add more listeners on the fly.
// Here you CAN use anonymous or closure function because .on() is only called once.
2024-08-06 20:05:35 +08:00
// ee.on('foo', async (c, payload) => {
// console.log('New foo created:', payload)
// })
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
export default ee
2024-07-07 15:03:10 +08:00
```
```js
// app.js
import { Hono } from 'hono'
2024-08-06 20:05:35 +08:00
import ee from './events'
2024-07-07 15:03:10 +08:00
// Initialize the app
const app = new Hono()
2024-08-06 20:05:35 +08:00
app.post('/users', async (c) => {
2025-03-19 16:53:11 +08:00
// ...
// Emit event and pass current context plus the payload
ee.emit(c, 'user:created', user)
// ...
2024-07-07 15:03:10 +08:00
})
2024-08-06 20:05:35 +08:00
app.delete('/users/:id', async (c) => {
2025-03-19 16:53:11 +08:00
// ...
// Emit event and pass current context plus the payload
await ee.emitAsync(c, 'user:deleted', id)
// ...
2024-07-07 15:03:10 +08:00
})
export default app
```
## Typescript
### 1. As hono middleware
```ts
// types.ts
import type { Emitter } from '@hono/event-emitter'
export type User = {
2025-03-19 16:53:11 +08:00
id: string
title: string
role: string
2024-07-07 15:03:10 +08:00
}
export type AvailableEvents = {
2025-03-19 16:53:11 +08:00
// event key: payload type
'user:created': User
'user:deleted': string
foo: { bar: number }
}
2024-07-07 15:03:10 +08:00
export type Env = {
2025-03-19 16:53:11 +08:00
Bindings: {}
Variables: {
// Define emitter variable type
emitter: Emitter< AvailableEvents >
}
}
2024-07-07 15:03:10 +08:00
```
```ts
// event-handlers.ts
import { defineHandlers } from '@hono/event-emitter'
import { AvailableEvents } from './types'
// Define event handlers
export const handlers = defineHandlers< AvailableEvents > ({
'user:created': [
2025-03-19 16:53:11 +08:00
(c, user) => {}, // c is current Context, payload will be correctly inferred as User
2024-07-07 15:03:10 +08:00
],
'user:deleted': [
2025-03-19 16:53:11 +08:00
async (c, payload) => {}, // c is current Context, payload will be inferred as string
],
2024-07-07 15:03:10 +08:00
})
// You can also define single event handler as named function using defineHandler to leverage typings
2024-08-06 20:05:35 +08:00
// export const fooHandler = defineHandler< AvailableEvents , ' foo ' > ((c, payload) => {
// // c is current Context, payload will be inferred as { bar: number }
2024-07-07 15:03:10 +08:00
// // ...
2024-08-06 20:05:35 +08:00
// console.log('Foo:', payload)
2024-07-07 15:03:10 +08:00
// })
```
```ts
// app.ts
import { emitter, type Emitter, type EventHandlers } from '@hono/event-emitter'
2024-08-06 20:05:35 +08:00
import { handlers, fooHandler } from './event-handlers'
2024-07-07 15:03:10 +08:00
import { Hono } from 'hono'
import { Env } from './types'
// Initialize the app
const app = new Hono< Env > ()
// Register the emitter middleware and provide it with the handlers
2024-08-06 20:05:35 +08:00
app.use(emitter(handlers))
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
// You can also add event listener inside middleware or route handler, but please only use named functions to prevent duplicates and memory leaks!
2024-07-07 15:03:10 +08:00
// app.use((c, next) => {
2024-08-06 20:05:35 +08:00
// c.get('emitter').on('foo', fooHandler)
2024-07-07 15:03:10 +08:00
// return next()
// })
// Routes
app.post('/user', async (c) => {
// ...
// Emit event and pass current context plus the payload (User type)
2024-08-06 20:05:35 +08:00
c.get('emitter').emit(c, 'user:created', user)
2024-07-07 15:03:10 +08:00
// ...
})
app.delete('/user/:id', async (c) => {
// ...
// Emit event and pass current context plus the payload (string)
2024-08-06 20:05:35 +08:00
await c.get('emitter').emitAsync(c, 'user:deleted', id)
2024-07-07 15:03:10 +08:00
// ...
})
export default app
```
2024-08-06 20:05:35 +08:00
The emitter is available in the context as `emitter` key.
As seen above (the commented out 'foo' event) you can also subscribe to events inside middlewares or route handlers,
but because middlewares are called on every request, you can only use named functions to prevent duplicates or memory leaks!
2024-07-07 15:03:10 +08:00
### 2. Standalone:
```ts
// types.ts
type User = {
2025-03-19 16:53:11 +08:00
id: string
title: string
2024-07-07 15:03:10 +08:00
role: string
}
type AvailableEvents = {
// event key: payload type
2025-03-19 16:53:11 +08:00
'user:created': User
'user:updated': User
'user:deleted': string
foo: { bar: number }
2024-07-07 15:03:10 +08:00
}
```
```ts
// events.ts
2025-03-19 16:53:11 +08:00
import {
createEmitter,
defineHandlers,
type Emitter,
type EventHandlers,
} from '@hono/event-emitter'
2024-07-07 15:03:10 +08:00
import { AvailableEvents } from './types'
// Define event handlers
export const handlers = defineHandlers< AvailableEvents > ({
'user:created': [
2025-03-19 16:53:11 +08:00
(c, user) => {}, // c is current Context, payload will be correctly inferred as User
2024-07-07 15:03:10 +08:00
],
'user:deleted': [
2025-03-19 16:53:11 +08:00
async (c, payload) => {}, // c is current Context, payload will be inferred as string
],
2024-07-07 15:03:10 +08:00
})
// You can also define single event handler using defineHandler to leverage typings
2024-08-06 20:05:35 +08:00
// export const fooHandler = defineHandler< AvailableEvents , ' foo ' > ((c, payload) => {})
2024-07-07 15:03:10 +08:00
// Initialize emitter with handlers
2024-08-06 20:05:35 +08:00
const ee = createEmitter(handlers)
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
// ee.on('foo', fooHandler)
2024-07-07 15:03:10 +08:00
// And you can add more listeners on the fly.
// Here you can use anonymous or closure function because .on() is only called once.
2025-03-19 16:53:11 +08:00
ee.on('foo', async (c, payload) => {
// Payload will be correctly inferred as User
console.log('User updated:', payload)
2024-07-07 15:03:10 +08:00
})
2024-08-06 20:05:35 +08:00
export default ee
2024-07-07 15:03:10 +08:00
```
```ts
// app.ts
2024-08-06 20:05:35 +08:00
import ee from './events'
2024-07-07 15:03:10 +08:00
import { Hono } from 'hono'
// Initialize the app
const app = new Hono()
app.post('/user', async (c) => {
// ...
// Emit event and pass current context plus the payload (User)
2024-08-06 20:05:35 +08:00
ee.emit(c, 'user:created', user)
2024-07-07 15:03:10 +08:00
// ...
})
app.delete('/user/:id', async (c) => {
// ...
// Emit event and pass current context plus the payload (string)
2025-03-19 16:53:11 +08:00
ee.emit(c, 'user:deleted', id)
2024-07-07 15:03:10 +08:00
// ...
})
export default app
```
2024-08-06 20:05:35 +08:00
## API Reference
### emitter
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
Creates a Hono middleware that adds an event emitter to the context.
```ts
function emitter< EPMap extends EventPayloadMap > (
2025-03-19 16:53:11 +08:00
eventHandlers?: EventHandlers< EPMap > ,
options?: EventEmitterOptions
2024-08-06 20:05:35 +08:00
): MiddlewareHandler
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `eventHandlers` - (optional): An object containing initial event handlers. Each key is event name and value is array of event handlers. Use `defineHandlers` function to create fully typed event handlers.
- `options` - (optional): An object containing options for the emitter. Currently, the only option is `maxHandlers` , which is the maximum number of handlers that can be added to an event. The default is `10` .
#### Returns
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
A Hono middleware function that adds an `Emitter` instance to the context under the key 'emitter'.
#### Example
```ts
2025-03-19 16:53:11 +08:00
app.use(emitter(eventHandlers))
2024-08-06 20:05:35 +08:00
```
### createEmitter
Creates new instance of event emitter with provided handlers. This is usefull when you want to use the emitter as standalone feature instead of Hono middleware.
```ts
function createEmitter< EPMap extends EventPayloadMap > (
2025-03-19 16:53:11 +08:00
eventHandlers?: EventHandlers< EPMap > ,
options?: EventEmitterOptions
2024-08-06 20:05:35 +08:00
): Emitter< EPMap >
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `eventHandlers` - (optional): An object containing initial event handlers. Each key is event name and value is array of event handlers.
- `options` - (optional): An object containing options for the emitter. Currently, the only option is `maxHandlers` , which is the maximum number of handlers that can be added to an event. The default is `10` .
#### Returns
An `Emitter` instance:
#### Example
```ts
2025-03-19 16:53:11 +08:00
const ee = createEmitter(eventHandlers)
2024-08-06 20:05:35 +08:00
```
### defineHandler
A utility function to define a typed event handler.
```ts
function defineHandler< EPMap extends EventPayloadMap , Key extends keyof EPMap , E extends Env = Env > (
2025-03-19 16:53:11 +08:00
handler: EventHandler< EPMap [ Key ] , E >
2024-08-06 20:05:35 +08:00
): EventHandler< EPMap [ Key ] , E >
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `handler` : The event handler function to be defined.
#### Type parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `EPMap` : The available event key to payload map i.e.: `type AvailableEvents = { 'user:created': { name: string } };` .
- `Key` : The key of the event type.
- `E` : (optional) - The Hono environment, so that the context within the handler has the right info.
#### Returns
The same event handler function with proper type inference.
#### Example
```ts
type AvailableEvents = {
2025-03-19 16:53:11 +08:00
'user:created': { name: string }
}
2024-08-06 20:05:35 +08:00
const handler = defineHandler< AvailableEvents , ' user:created ' > ((c, payload) => {
2025-03-19 16:53:11 +08:00
console.log('New user created:', payload)
2024-08-06 20:05:35 +08:00
})
```
### defineHandlers
A utility function to define multiple typed event handlers.
```ts
2025-03-19 16:53:11 +08:00
function defineHandlers< EPMap extends EventPayloadMap , E extends Env = Env > (handlers: {
[K in keyof EPMap]?: EventHandler< EPMap [ K ] , E > []
}): { [K in keyof EPMap]?: EventHandler< EPMap [ K ] , E > [] }
2024-08-06 20:05:35 +08:00
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `handlers` : An object containing event handlers for multiple event types/keys.
#### Type parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `EPMap` : The available event key to payload map i.e.: `type AvailableEvents = { 'user:created': { name: string } };` .
- `E` : (optional) - The Hono environment, so that the context within the handler has the right info.
#### Returns
The same handlers object with proper type inference.
#### Example
```ts
type AvailableEvents = {
2025-03-19 16:53:11 +08:00
'user:created': { name: string }
}
2024-08-06 20:05:35 +08:00
const handlers = defineHandlers< AvailableEvents > ({
2025-03-19 16:53:11 +08:00
'user:created': [
(c, payload) => {
console.log('New user created:', pyload)
},
],
2024-08-06 20:05:35 +08:00
})
```
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
## Emitter instance methods
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
The `Emitter` interface provides methods for managing and triggering events. Here's a detailed look at each method:
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
### on
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
Adds an event handler for the specified event key.
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
#### Signature
2024-07-07 15:03:10 +08:00
2024-08-06 20:05:35 +08:00
```ts
function on< Key extends keyof EventPayloadMap > (
2025-03-19 16:53:11 +08:00
key: Key,
handler: EventHandler< EventPayloadMap [ Key ] >
2024-08-06 20:05:35 +08:00
): void
```
#### Parameters
- `key` : The event key to listen for. Must be a key of `EventHandlerPayloads` .
- `handler` : The function to be called when the event is emitted. If using within a Hono middleware or request handler, do not use anonymous or closure functions!
It should accept two parameters:
2025-03-19 16:53:11 +08:00
- `c` : The current Hono context object.
- `payload` : The payload passed when the event is emitted. The type of the payload is inferred from the `EventHandlerPayloads` type.
2024-08-06 20:05:35 +08:00
#### Returns
`void`
#### Example
Using outside the Hono middleware or request handler:
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
```ts
type AvailableEvents = {
2025-03-19 16:53:11 +08:00
'user:created': { name: string }
}
const ee = createEmitter< AvailableEvents > ()
2024-08-06 20:05:35 +08:00
// If adding event handler outside of Hono middleware or request handler, you can use both, named or anonymous function.
ee.on('user:created', (c, user) => {
2025-03-19 16:53:11 +08:00
console.log('New user created:', user)
2024-08-06 20:05:35 +08:00
})
```
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
Using within Hono middleware or request handler:
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
```ts
type AvailableEvents = {
2025-03-19 16:53:11 +08:00
'user:created': { name: string }
}
2024-08-06 20:05:35 +08:00
// Define event handler as named function, outside of the Hono middleware or request handler to prevent duplicates/memory leaks
const namedHandler = defineHandler< AvailableEvents , ' user:created ' > ((c, user) => {
2025-03-19 16:53:11 +08:00
console.log('New user created:', user)
2024-08-06 20:05:35 +08:00
})
2025-03-19 16:53:11 +08:00
app.use(emitter< AvailableEvents > ())
2024-08-06 20:05:35 +08:00
app.use((c, next) => {
2025-03-19 16:53:11 +08:00
c.get('emitter').on('user:created', namedHandler)
return next()
2024-08-06 20:05:35 +08:00
})
```
### off
Removes an event handler for the specified event key.
#### Signature
```ts
function off< Key extends keyof EventPayloadMap > (
2025-03-19 16:53:11 +08:00
key: Key,
handler?: EventHandler< EventPayloadMap [ Key ] >
2024-08-06 20:05:35 +08:00
): void
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `key` : The event key to remove the handler from. Must be a key of `EventPayloadMap` .
- `handler` (optional): The specific handler function to remove. If not provided, all handlers for the given key will be removed.
#### Returns
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
`void`
#### Example
```ts
type AvailableEvents = {
2025-03-19 16:53:11 +08:00
'user:created': { name: string }
}
2024-08-06 20:05:35 +08:00
2025-03-19 16:53:11 +08:00
const ee = createEmitter< AvailableEvents > ()
2024-08-06 20:05:35 +08:00
const logUser = defineHandler< AvailableEvents , ' user:created ' > ((c, user) => {
2025-03-19 16:53:11 +08:00
console.log(`User: ${user.name}`)
})
2024-08-06 20:05:35 +08:00
2025-03-19 16:53:11 +08:00
ee.on('user:created', logUser)
2024-08-06 20:05:35 +08:00
// Later, to remove the specific handler:
2025-03-19 16:53:11 +08:00
ee.off('user:created', logUser)
2024-08-06 20:05:35 +08:00
// Or to remove all handlers for 'user:created':
2025-03-19 16:53:11 +08:00
ee.off('user:created')
2024-08-06 20:05:35 +08:00
```
### emit
Synchronously emits an event with the specified key and payload.
#### Signature
```ts
emit< Key extends keyof EventPayloadMap > (
c: Context,
key: Key,
payload: EventPayloadMap[Key]
): void
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `c` : The current Hono context object.
- `key` : The event key to emit. Must be a key of `EventPayloadMap` .
- `payload` : The payload to pass to the event handlers. The type of the payload is inferred from the `EventPayloadMap` type.
#### Returns
`void`
#### Example
```ts
app.post('/users', (c) => {
2025-03-19 16:53:11 +08:00
const user = { name: 'Alice' }
c.get('emitter').emit(c, 'user:created', user)
})
2024-08-06 20:05:35 +08:00
```
### emitAsync
Asynchronously emits an event with the specified key and payload.
#### Signature
```ts
emitAsync< Key extends keyof EventPayloadMap > (
c: Context,
key: Key,
payload: EventPayloadMap[Key],
options?: EmitAsyncOptions
): Promise< void >
```
#### Parameters
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- `c` : The current Hono context object.
- `key` : The event key to emit. Must be a key of `EventPayloadMap` .
- `payload` : The payload to pass to the event handlers. The type of the payload is inferred from the `EventPayloadMap` type.
- `options` (optional): An object containing options for the asynchronous emission.
Currently, the only option is `mode` , which can be `'concurrent'` (default) or `'sequencial'` .
2025-03-19 16:53:11 +08:00
- The `'concurrent'` mode will call all handlers concurrently (at the same time) and resolve or reject (with aggregated errors) after all handlers settle.
- The `'sequencial'` mode will call handlers one by one and resolve when all handlers are done or reject when the first error is thrown, not executing rest of the handlers.
2024-08-06 20:05:35 +08:00
#### Returns
`Promise<void>`
#### Example
```ts
app.post('/users', async (c) => {
2025-03-19 16:53:11 +08:00
const user = { name: 'Alice' }
await c.get('emitter').emitAsync(c, 'user:created', user)
// await c.get('emitter').emitAsync(c, 'user:created', user, { mode: 'sequencial' });
})
2024-08-06 20:05:35 +08:00
```
## Types
### EventKey
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
A string literal type representing an event key.
```ts
type EventKey = string | symbol
```
### EventHandler
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
A function type that handles an event.
```ts
type EventHandler< T , E extends Env = Env > = (c: Context< E > , payload: T) => void | Promise< void >
```
### EventHandlers
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
An object type containing event handlers for multiple event types/keys.
```ts
type EventHandlers< T , E extends Env = Env > = { [K in keyof T]?: EventHandler< T [ K ] , E > [] }
```
### EventPayloadMap
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
An object type containing event keys and their corresponding payload types.
```ts
type EventPayloadMap = Record< EventKey , any >
```
### EventEmitterOptions
An object type containing options for the `Emitter` class.
```ts
2025-03-19 16:53:11 +08:00
type EventEmitterOptions = { maxHandlers?: number }
2024-08-06 20:05:35 +08:00
```
### EmitAsyncOptions
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
An object type containing options for the `emitAsync` method.
```ts
type EmitAsyncOptions = {
2025-03-19 16:53:11 +08:00
mode?: 'concurrent' | 'sequencial'
2024-08-06 20:05:35 +08:00
}
```
### Emitter
An interface representing an event emitter.
```ts
interface Emitter< EventPayloadMap > {
2025-03-19 16:53:11 +08:00
on< Key extends keyof EventPayloadMap > (key: Key, handler: EventHandler< EventPayloadMap [ Key ] > ): void
off< Key extends keyof EventPayloadMap > (
key: Key,
handler?: EventHandler< EventPayloadMap [ Key ] >
): void
emit< Key extends keyof EventPayloadMap > (c: Context, key: Key, payload: EventPayloadMap[Key]): void
emitAsync< Key extends keyof EventPayloadMap > (
c: Context,
key: Key,
payload: EventPayloadMap[Key],
options?: EmitAsyncOptions
): Promise< void >
2024-08-06 20:05:35 +08:00
}
```
2024-07-07 15:03:10 +08:00
For more usage examples, see the [tests ](src/index.test.ts ) or [Hono REST API starter kit ](https://github.com/DavidHavl/hono-rest-api-starter )
2024-08-06 20:05:35 +08:00
## FAQ
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### What the heck is event emitter and why should I use it?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
Event emitter is a pattern that allows you to decouple your code and make it more modular and maintainable.
It's a way to implement the observer pattern in your application.
It's especially useful in larger projects or projects with a lot of interactions between features.
Just imagine you have a user registration feature, and you want to send a welcome email after the user is created. You can do this by emitting an event `user:created` and then listen to this event in another part of your application (e.g. email service).
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### How is this different to the built-in EventEmitter in Node.js?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
The build-in EventEmitter has huge API surface, weak TypeScript support and does only synchronous event emitting. Hono's event emitter is designed to be minimal, lightweight, edge compatible and fully typed. Additionally, it supports async event handlers.
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### Is there a way to define event handlers with types?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
Yes, you can use `defineHandlers` and `defineHandler` functions to define event handlers with types. This way you can leverage TypeScript's type inference and get better type checking.
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### Does it support async event handlers?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
Yes, it does. You can use async functions as event handlers and emit the events using `emitAsync` method.
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### What happens if I emit an event that has no handlers?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
Nothing. The event will be emitted, but no handlers will be called.
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### Using `emitAsync` function, what happens if one or more of the handlers reject?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
- If using `{ mode = 'concurrent' }` in the options (which is the default), it will call all handlers concurrently (at the same time) and resolve or reject (with aggregated errors) after all handlers settle.
- If using `{ mode = 'sequencial' }` in the options, it will call handlers one by one and resolve when all handlers are done or reject when the first error is thrown, not executing rest of the handlers.
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### Is it request scoped?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
No, by design it's not request scoped. The same Emitter instance is shared across all requests.
This aproach prevents memory leaks (especially when using closures or dealing with large data structures within the handlers) and additional strain on Javascript garbage collector.
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
### Why can't I use anonymous functions or closures as event handlers when adding them inside of middleware?
2025-03-19 16:53:11 +08:00
2024-08-06 20:05:35 +08:00
This is because middleware or request handlers run repeatedly on every request, and because anonymous functions are created as new unique object in memory every time,
you would be instructing the event emitter to add new handler for same key every time the request/middleware runs.
Since they are each different objects in memory they can't be checked for equality and would result in memory leaks and duplicate handlers.
You should use named functions if you really want to use the `on()` method inside of middleware or request handler.
2025-03-19 16:53:11 +08:00
2024-07-07 15:03:10 +08:00
## Author
2024-08-06 20:05:35 +08:00
David Havl < https: / / github . com / DavidHavl >
2024-07-07 15:03:10 +08:00
## License
MIT