Skip to content
Jezz Santos edited this page Mar 10, 2017 · 33 revisions

What are Webhooks?

In general, Webhooks are a common asynchronous means today for listening to key 'events' raised by online systems, typically over HTTPS.

The idea is straightforward:

  1. Register a callback URL to be called when an "event" of a certain type is going to be raised, and the system will POST you notification of that event (along with any data describing that event) to that callback URL when the event is raised.
  2. You first need to register the callback URL with the system for a specific event (which implies a specific DTO of the event data).
  3. Sit back and wait to be called at your callback URL.

Under the covers, Webhooks are a good example of pub/sub architectures, where the consumers of the events are (typically) decoupled from the producer of the events. It is generally an asynchronous eventing system for the web.

How do ServiceStack.Webhooks work?

In this project, we are basing the subscription model of webhooks on that defined by GitHub Webhooks.

Which, defines the subscription service interface, and the wire representations of published events.

The WebhookFeature will be a ServiceStack Plugin that installs the following:

  1. A fully implemented and secured SubscriptionService, that allows any subscriber to register a webhook to any event raised by any service (in the same AppHost).
  2. A singleton service IWebhooks that will be used to raise any event from anywhere in any service in the AppHost.
  3. An extensible framework where the developer can plugin selected technologies that fit their service architecture and runtime environment.
  4. Default implementations of the key components in the architecture to support the delivery of events to registered subscribers.

How do I wire-up ServiceStack.Webhooks to my services?

You add webhooks to your ServiceStack project by simply registering (and customizing) the WebhookFeature in your AppHost.cs.

public override void Configure(Container container)
{
    // Register the ValidationFeature and AuthFeature first
    Plugins.Add(new ValidationFeature());
    Plugins.Add(new AuthFeature(...));

    Plugins.Add(new WebhookFeature
    {
        .. any customization for your environment
    });
}

The WebhookFeature comes with a built-in ISubscriptionStore and a built-in IEventSink that you can use to get up an running quickly.

WARNING: In any production environment you are going to need to register a ISubscriptionStore and IEventSink that suits your architecture, since the built-in ones are really only designed for getting up and running quickly, and for testing.

public override void Configure(Container container)
{
    // Register the ValidationFeature and AuthFeature first
    Plugins.Add(new ValidationFeature());
    Plugins.Add(new AuthFeature(...));

    // Register your own ISubscriptionStore and IEventSink
    container.Register<ISubscriptionStore>(new MyDbSubscriptionStore());
    container.Register<IEventSink>(new MyAsyncEventSink());

    Plugins.Add(new WebhookFeature
    {
        .. any customization for your environment
    });
}

See Getting Started for more details

How can I extend the WebhookFeature to suit my architecture?

There are several points of extensibility in the WebhookFeature, and each may support their own customization:

Subscription Service

By default the WebHookFeature registers and configures the SubscriptionService to provide an API for subscribers to manage their webhooks.

You can choose to turn this service off, and ship your own, and you can also configure the authorization roles that secure it.

See Subscription Service for more details on how to customize it

Subscription Store

Subscriptions for webhooks need to be stored (ISubscriptionStore) after a user of your service subscribes to a webhook using the API: POST /webhooks/subscriptions.

You specify your own store by registering it in the IOC container. If you specify no store, the default MemorySubscriptionStore will be used, which is fine for testing, but beware that your subscriptions will be lost whenever your AppHost is restarted.

You should register a different ISubscriptionStore that will persist subscriptions to some permanent (and perhaps distributed) store in your architecture. These stores are typically provided by extensions to the ServiceStack.Webhooks framework such as those listed in: Plugins

public override void Configure(Container container)
{
    // Register your own subscription store
    container.Register<ISubscriptionStore>(new MyDbSubscriptionStore());

    Plugins.Add(new WebhookFeature();
}

WARNING: The MemorySubscriptionStore is not designed for use in production systems. If you do NOT register your own ISubscriptionStore your subscriptions will be lost when your AppHost restarts!

Event Sink

When events are raised they are passed to the event sink (IEventSink), typically temporarily, until they are "relayed" to all registered subscribers for that event. Remember that raising a single event might result in (potentially) hundreds of subscribers being notified!

Ideally, for scalability and performance, you would not want to notify all those subscribers on the same thread that raised the event. Probably not even by the same process that raised them. Ideally, that workload is passed onto another process while your services stay responsive.

You specify your own sink by registering it in the IOC container. If you specify no sink, the default AppHostEventSink will be used, which is fine for testing, but beware that this sink works synchronously on the same thread that raises the event, and so it is not optimal for scale in production systems.

The AppHostEventSink will relay events to all subscribers in the same thread that called IWebhooks.Publish<TDto>().

You should register a different IEventSink that will decouple the raising of events from the relays of events to subscribers, using appropriate components in your architecture. (i.e. muti-threading, queues, async hooks etc.)

These kinds of sinks are typically provided by extensions to the ServiceStack.Webhooks framework such as those listed in: Plugins

public override void Configure(Container container)
{
    // Register your own event store
    container.Register<IEventSink>(new MyDbEventSink());

    Plugins.Add(new WebhookFeature();
}

WARNING: The AppHostEventSink is not designed for use in production systems. If you do NOT register your own IEventSink your services will suffer unacceptable performance penalties.