Skip to content

Commit cac9766

Browse files
authored
feat: first example with services (#1)
* feat: first example with services * chore: connect on service start * chore: readme updated
1 parent 1e074a6 commit cac9766

12 files changed

Lines changed: 347 additions & 107 deletions

File tree

README.md

Lines changed: 107 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The `@nextorders/queue` is a TypeScript library designed to simplify working wit
66

77
![RabbitMQ Dashboard](https://github.com/user-attachments/assets/b330e911-bc74-404d-9999-c720b04ca9f7)
88

9-
## Key Features
9+
## 😨 Key Features
1010

1111
- Type-safe operations with message queues
1212
- Automatic connection to the RabbitMQ server
@@ -15,97 +15,165 @@ The `@nextorders/queue` is a TypeScript library designed to simplify working wit
1515
- Support for various message types
1616
- Flexible connection configuration
1717

18-
## Installation
18+
## 📦 Installation
1919

2020
You can install the library via npm:
2121

2222
```bash
2323
npm install @nextorders/queue
2424
```
2525

26-
## Usage
26+
## 🚀 Usage
2727

28-
Prepare types:
28+
### 1. Define Event Types
29+
30+
Create type definitions for your events:
2931

3032
```typescript
3133
import type { BaseEventMessage } from '@nextorders/queue'
3234

3335
export enum Events {
34-
TICKET_MESSAGE_CREATED = 'ticketMessageCreated',
35-
OTHER_ACTION = 'otherAction',
36+
UserCreated = 'userCreated',
37+
EmailSent = 'emailSent',
3638
}
3739

38-
export type EventMessage = BaseEventMessage<Events>
40+
export type EventMessage = UserCreated | EmailSent
41+
42+
export interface UserCreated extends BaseEventMessage {
43+
event: typeof Events.UserCreated
44+
data: {
45+
id: string
46+
name: string
47+
email: string
48+
}
49+
}
3950

40-
export interface TicketMessageCreated extends EventMessage {
41-
type: typeof Events.TICKET_MESSAGE_CREATED
51+
export interface EmailSent extends BaseEventMessage {
52+
event: typeof Events.EmailSent
4253
data: {
43-
ticketId: string
44-
ticketOwnerId: string
45-
messageId: string
46-
userId: string
47-
userName: string
48-
userSurname: string | undefined
49-
userText: string
54+
email: string
5055
}
5156
}
5257
```
5358

54-
Create Entities:
59+
### 2. Create Entities
60+
61+
Define entities that represent your services:
5562

5663
```typescript
5764
import { Entity, Repository } from '@nextorders/queue'
65+
import { Events } from './types'
66+
67+
export class User extends Entity {
68+
constructor(repository: Repository) {
69+
super({
70+
name: 'user',
71+
eventsToConsume: [],
72+
repository,
73+
})
74+
}
75+
}
5876

59-
export class Telegram extends Entity {
77+
export class Email extends Entity {
6078
constructor(repository: Repository) {
6179
super({
62-
name: 'telegram',
63-
eventsToConsume: ['ticketMessageCreated'],
80+
name: 'email',
81+
eventsToConsume: [Events.UserCreated],
6482
repository,
6583
})
6684
}
6785
}
6886
```
6987

70-
Create Repository with Entities:
88+
### 3. Create Repository
89+
90+
Create a repository that manages your entities:
7191

7292
```typescript
93+
import type { EventMessage } from './types'
7394
import { Repository } from '@nextorders/queue'
74-
import { Telegram, Ticket } from './entities'
95+
import { Email, User } from './entities'
7596

7697
class QueueRepository extends Repository {
77-
telegram: Telegram = new Telegram(this)
78-
ticket: Ticket = new Ticket(this)
98+
user: User = new User(this)
99+
email: Email = new Email(this)
100+
101+
// Override publish method with proper typing
102+
publish<T extends EventMessage>(
103+
event: T['event'],
104+
data: T['data']
105+
): Promise<void> {
106+
return super.publish(event, data)
107+
}
79108
}
80109

81110
export const repository = new QueueRepository()
82111
```
83112

84-
On server start (for example in Nuxt server plugin):
113+
### 4. Connect to RabbitMQ
114+
115+
On service start, connect to your RabbitMQ instance:
85116

86117
```typescript
87-
await repository.connect(process.env.QUEUE_URL)
118+
import { repository } from './repository'
119+
120+
await repository.connect('amqp://guest:guest@localhost:5672')
121+
```
122+
123+
### 5. Publish Events
124+
125+
Create and publish events from your services:
126+
127+
```typescript
128+
await repository.publish(Events.UserCreated, {
129+
id: newUser.id,
130+
name: newUser.name,
131+
email: newUser.email,
132+
})
88133
```
89134

90-
And thats it! Use repository in app:
135+
### 6. Consume Events
136+
137+
Subscribe to events and handle them:
91138

92139
```typescript
93-
// Publisher
94-
await repository.publisher.send({
95-
exchange: repository.exchanges.events.exchange,
96-
routingKey: Events.TICKET_MESSAGE_CREATED,
97-
}, body)
98-
99-
// Consumer
100-
await repository.telegram.consume(async (msg) => {
101-
if (msg.type === 'ticketMessageCreated') {
102-
return handleTicketMessageCreated(msg) // ack on finish
140+
import type { UserCreated } from './types'
141+
import { repository } from './repository'
142+
import { Events } from './types'
143+
144+
// Define event handlers
145+
async function handleUserCreated(data: UserCreated['data']): Promise<boolean> {
146+
try {
147+
await sendEmail(data.email)
148+
return true
149+
} catch (error) {
150+
console.error('Error handling UserCreated event:', error)
151+
return false
103152
}
153+
}
154+
155+
async function sendEmail(email: string): Promise<void> {
156+
console.warn('Sending email to:', email)
157+
158+
// Publish EmailSent event
159+
await repository.publish(Events.EmailSent, { email })
160+
}
104161

105-
return queue.ignore()
162+
// Subscribe to events
163+
await repository.consume(repository.email.name, {
164+
[Events.UserCreated]: handleUserCreated,
106165
})
107166
```
108167

109-
## License
168+
## 💁‍♂️ Example: Microservices Architecture
169+
170+
Check out the examples/microservices directory for a complete working example with:
171+
172+
- Service 1: User creation service
173+
- Service 2: Email notification service
174+
- Shared repository with entities
175+
- Type-safe event definitions
176+
177+
## 🤝 License
110178

111179
This project is licensed under the MIT License.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Repository } from '@nextorders/queue'
2+
import { Entity } from '@nextorders/queue'
3+
import { Events } from '../types'
4+
5+
export class Email extends Entity {
6+
constructor(repository: Repository) {
7+
super({
8+
name: 'email',
9+
eventsToConsume: [
10+
Events.UserCreated,
11+
],
12+
repository,
13+
})
14+
}
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { Repository } from '@nextorders/queue'
2+
import { Entity } from '@nextorders/queue'
3+
4+
export class User extends Entity {
5+
constructor(repository: Repository) {
6+
super({
7+
name: 'user',
8+
eventsToConsume: [],
9+
repository,
10+
})
11+
}
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { EventMessage } from './types'
2+
import { Repository } from '@nextorders/queue'
3+
import { Email } from './entities/email'
4+
import { User } from './entities/user'
5+
6+
class QueueRepository extends Repository {
7+
user = new User(this)
8+
email = new Email(this)
9+
10+
async publish<T extends EventMessage>(event: T['event'], data: T['data']) {
11+
return super.publish(event, data)
12+
}
13+
}
14+
15+
export const repository = new QueueRepository()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { BaseEventMessage, Status } from '@nextorders/queue'
2+
3+
// All possible events
4+
export enum Events {
5+
UserCreated = 'userCreated',
6+
EmailSent = 'emailSent',
7+
}
8+
9+
export type EventMessage = UserCreated | EmailSent
10+
11+
export type EventHandler = (msg: EventMessage) => Promise<Status>
12+
export type EventMessageHandler<T = EventMessage['data']> = (data: T) => Promise<boolean>
13+
14+
export type EventHandlerMap = Record<EventMessage['event'], EventMessageHandler>
15+
16+
export interface UserCreated extends BaseEventMessage {
17+
event: typeof Events.UserCreated
18+
data: {
19+
id: string
20+
name: string
21+
email: string
22+
}
23+
}
24+
25+
export interface EmailSent extends BaseEventMessage {
26+
event: typeof Events.EmailSent
27+
data: {
28+
email: string
29+
}
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { repository } from '../repository'
2+
import { Events } from '../repository/types'
3+
4+
// Service 1: User Service
5+
const body = {
6+
name: 'John Doe',
7+
email: '5Tt9o@example.com',
8+
}
9+
10+
// DB: Save user in database
11+
const newUser = {
12+
id: '123',
13+
...body,
14+
}
15+
16+
// Publish Event for other services
17+
repository.publish(Events.UserCreated, {
18+
id: newUser.id,
19+
name: newUser.name,
20+
email: newUser.email,
21+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// On service start
2+
import { repository } from '../repository'
3+
4+
// Connect to RabbitMQ
5+
repository.connect('amqp://guest:guest@localhost:5672')
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { EventHandlerMap, UserCreated } from '../repository/types'
2+
import { repository } from '../repository'
3+
import { Events } from '../repository/types'
4+
5+
// Service 2: Email Service
6+
7+
// Consume to Events
8+
repository.consume(repository.email.name, {
9+
[Events.UserCreated]: handleUserCreated,
10+
} as EventHandlerMap)
11+
12+
// Business logic
13+
async function handleUserCreated(data: UserCreated['data']): Promise<boolean> {
14+
try {
15+
// Service logic: Send email
16+
await sendEmail(data.email)
17+
return true
18+
} catch (error) {
19+
console.error('Failed to send email:', error)
20+
return false
21+
}
22+
}
23+
24+
async function sendEmail(email: string) {
25+
console.warn('Sending email to', email)
26+
27+
// Publish Event for other services
28+
await repository.publish(Events.EmailSent, {
29+
email,
30+
})
31+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// On service start
2+
import { repository } from '../repository'
3+
4+
// Connect to RabbitMQ
5+
repository.connect('amqp://guest:guest@localhost:5672')

0 commit comments

Comments
 (0)