Fan-Out Messaging with URL Groups
Learn how to broadcast messages to multiple endpoints simultaneously using QueueSaaS URL Groups. Perfect for microservices architectures and event-driven systems.
Fan-Out Messaging with URL Groups
URL Groups are one of QueueSaaS’s most powerful features for microservices architectures. They allow you to publish a single message that gets delivered to multiple endpoints simultaneously, perfect for event-driven systems, webhook broadcasting, and decoupled service communication.
What Are URL Groups?
URL Groups let you define a collection of endpoints and publish messages to all of them with a single API call. Each endpoint receives the message independently, with its own retry logic and delivery tracking.
Use cases:
- Broadcasting events to multiple microservices
- Sending webhooks to multiple third-party services
- Notifying multiple systems of state changes
- Implementing pub/sub patterns
Creating a URL Group
Basic Setup
import { Client } from '@anlyonhq/scheduler';
const client = new Client({
apiKey: process.env.ANLYON_API_KEY!,
});
// Create a URL group with multiple endpoints
const group = await client.urlGroups.create({
name: 'Production Webhooks',
endpoints: [
{
url: 'https://service1.example.com/webhook',
name: 'User Service',
},
{
url: 'https://service2.example.com/webhook',
name: 'Notification Service',
},
{
url: 'https://service3.example.com/webhook',
name: 'Analytics Service',
},
],
});
console.log(`Created group: ${group.data.id}`);
Custom Retry Configuration
Each endpoint can have its own retry configuration:
const group = await client.urlGroups.create({
name: 'Critical Services',
endpoints: [
{
url: 'https://critical-service.com/webhook',
name: 'Critical Service',
retryCount: 10, // More retries for critical services
},
{
url: 'https://optional-service.com/webhook',
name: 'Optional Service',
retryCount: 3, // Fewer retries for optional services
},
],
});
Publishing to a Group
Basic Broadcast
// Publish to all endpoints in the group
const result = await client.urlGroups.publish(group.data.id, {
method: 'POST',
body: {
event: 'user.created',
userId: '123',
timestamp: new Date().toISOString(),
},
});
console.log(`Published to ${result.data.total} endpoints`);
console.log(`Successful: ${result.data.successful}`);
console.log(`Failed: ${result.data.failed}`);
With Delay
You can also add a delay before broadcasting:
await client.urlGroups.publish(group.data.id, {
method: 'POST',
body: { event: 'order.processed', orderId: '456' },
delay: 60, // Broadcast 60 seconds from now
});
Managing Groups
List All Groups
const groups = await client.urlGroups.list();
groups.data.forEach(group => {
console.log(`${group.name}: ${group.id}`);
});
Get Group Details
const group = await client.urlGroups.get('group-id');
console.log(`Group: ${group.data.name}`);
console.log(`Endpoints: ${group.data.endpoints.length}`);
group.data.endpoints.forEach(endpoint => {
console.log(` - ${endpoint.name}: ${endpoint.url}`);
});
Add Endpoints
Dynamically add endpoints to an existing group:
await client.urlGroups.addEndpoint('group-id', {
url: 'https://new-service.com/webhook',
name: 'New Service',
retryCount: 5,
});
Remove Endpoints
await client.urlGroups.removeEndpoint('group-id', 'endpoint-id');
Update Group Name
await client.urlGroups.update('group-id', {
name: 'Updated Group Name',
});
Delete a Group
await client.urlGroups.delete('group-id');
Real-World Examples
Event-Driven Architecture
In a microservices architecture, URL Groups make it easy to broadcast events:
// When a user signs up, notify multiple services
const userCreatedGroup = await client.urlGroups.get('user-events-group');
await client.urlGroups.publish(userCreatedGroup.data.id, {
method: 'POST',
body: {
event: 'user.created',
userId: newUser.id,
email: newUser.email,
timestamp: new Date().toISOString(),
},
});
// This automatically notifies:
// - Email service (send welcome email)
// - Analytics service (track signup)
// - Notification service (send push notification)
// - CRM service (create contact)
Third-Party Webhook Broadcasting
Broadcast webhooks to multiple third-party services:
// Create a group for payment webhooks
const paymentGroup = await client.urlGroups.create({
name: 'Payment Webhooks',
endpoints: [
{ url: 'https://stripe.com/webhook', name: 'Stripe' },
{ url: 'https://paypal.com/webhook', name: 'PayPal' },
{ url: 'https://internal-api.com/payments', name: 'Internal API' },
],
});
// Broadcast payment event
await client.urlGroups.publish(paymentGroup.data.id, {
method: 'POST',
body: {
event: 'payment.completed',
amount: 99.99,
currency: 'USD',
transactionId: 'txn_123',
},
});
Multi-Region Deployment
Notify services across multiple regions:
const multiRegionGroup = await client.urlGroups.create({
name: 'Multi-Region Services',
endpoints: [
{ url: 'https://us-east.api.example.com/webhook', name: 'US East' },
{ url: 'https://eu-west.api.example.com/webhook', name: 'EU West' },
{ url: 'https://asia-pac.api.example.com/webhook', name: 'Asia Pacific' },
],
});
// Broadcast configuration update
await client.urlGroups.publish(multiRegionGroup.data.id, {
method: 'POST',
body: {
event: 'config.updated',
config: { featureFlag: true },
},
});
Best Practices
- Group by purpose: Create groups based on event types or service categories
- Use descriptive names: Make it easy to identify groups in your dashboard
- Set appropriate retries: Critical services need more retries than optional ones
- Monitor delivery: Track success rates for each endpoint
- Handle failures gracefully: Some endpoints may fail while others succeed
- Limit group size: Keep groups under 50 endpoints for optimal performance
- Use idempotent endpoints: Ensure your webhooks can handle duplicate deliveries
Error Handling
When publishing to a group, some endpoints may succeed while others fail:
const result = await client.urlGroups.publish('group-id', {
body: { event: 'test' },
});
if (result.data.failed > 0) {
console.warn(`${result.data.failed} endpoints failed to receive the message`);
// Check individual endpoint status
result.data.results.forEach(endpointResult => {
if (!endpointResult.success) {
console.error(`Failed: ${endpointResult.endpointUrl} - ${endpointResult.error}`);
}
});
}
Performance Considerations
- Parallel delivery: All endpoints receive messages in parallel
- Independent retries: Each endpoint retries independently
- Non-blocking: Failed endpoints don’t block successful ones
- Scalable: Groups can handle high message volumes
Comparison: URL Groups vs Individual Messages
Without URL Groups (Manual)
// Publish to each endpoint individually
const endpoints = [
'https://service1.com/webhook',
'https://service2.com/webhook',
'https://service3.com/webhook',
];
for (const url of endpoints) {
await client.messages.publish({
url,
body: { event: 'test' },
});
}
// Problems:
// - More API calls
// - Harder to manage
// - No aggregate tracking
With URL Groups (Recommended)
// Single API call broadcasts to all
await client.urlGroups.publish('group-id', {
body: { event: 'test' },
});
// Benefits:
// - Single API call
// - Centralized management
// - Aggregate tracking
// - Independent retries
Next Steps
- Learn about CRON scheduling
- Explore Dead Letter Queue management
- Check out our TypeScript SDK guide
Start broadcasting with URL Groups today! 📡