On Github kyletolle / dsw_webhooks_talk
at this session online using
Software Engineer at Spatial Networks
Implemented webhooks in Fulcrum, our mobile data collection service
Ours have been in operation for about a year and a half. I wanted to share what I've learned. Maybe you'll find it useful or interesting. And I would love to get your feedback as well. To date, our system has handled over 11.3 million webhook requests.https://kyletolle.github.io/dsw_webhooks_talk
Government Agency for Bear Attacks
This fictional organization will help make things a little more concrete. People in the office will specify what kind of data they want to collect about bear attacks. People in the field, like forest rangers, will be the ones collecting the data about the bear attacks.to talk about webhooks
we need some background
what we know
They fit you into their workflow
More likely to use your service
When you don't dictate your customer's workflow, they have an easier time adopting your service into how they do things. They can slot us in, without rearchitecting the world. This is good, and the incumbent likely doesn't have this flexibility.What’s this mean in practice?
They can hook you up to other services
Customers don't mind paying as long as they can get their data out. You're more valuable to them by being accessible. They stay nimble, and can get more done.This is a good way to
&
But you have to predict it
And then maintain it
This is one option. Can be powerful and valuable. More effort initially. Integrations are limited to what you have built.Instead, what about…
Not hardcoding for the service-du-jour
Integrate with other services that are relevant later
Since you're not tying yourself to what's hot now, you can integrate more easily with services that don't even exist yet. And adding these integrations will be easier and cheaper.Gives customers flexibility to do anything they want
Fit into their business and workflow over time
Only the customer really knows what they're trying to do and how to best do it.Make it easy to understand and get started with
Explaining your system helps you understand it better too.Also helpful when you do integration work for clients
As web developers
are we already familiar with?
This is
But there are some
someone murders your servers?
They're likely to push the limits. Maybe they'll write an infinite loop which hits your API as fast as it can. Or, they'll have some "mission critical" need for ONE MILLION calls per month. That's not a made up number. We had someone whose script really did this. It's bad enough if one person does this, but imagine a few more people doing the same thing. These legitimate requests can end up swamping your servers.&
But it’s only one approach
Because, if we can do this…
And that means happier customers
Maybe even some extra revenue
We know that polling is
But what about the other way around?
to see if data changes
when data has changed
since it’s common infrastructure
Same as how APIs work. No need to reinvent the wheel here.&
When an event happens,
to our client’s URL
and deliver some data
This is Real-Time™
This is what I mean by
We’ll see real-world examples later
Start small & build momentum
We’ll have a client server running, Polis.rb
A small web server which logs POST data
require_relative '../lib/webhooks'
class RandomNumberGenerator
  include Webhooks
  def initialize
    self.webhooks = %w{http://localhost:9000}
  end
  def generate
    random_number = SecureRandom.random_number(1000)
    send_webhooks(:random_number, :generate, random_number)
    return random_number
  end
end
require './examples/random_number_generator.rb' > puts RandomNumberGenerator.new.generate => 863
We see the webhook request
$ PORT=9000 bf
...
Logging POST request:
...
Body:
{"event":"random_number:generate","data":863}
...
I've omitted some of the output, to maintain focus on what' important: the
random number it received.
require 'net/http'
require 'securerandom'
require 'json'
module Webhooks
  attr_accessor :webhooks
  def send_webhooks(resource, action, data)
    event_type = "#{resource}:#{action}"
    payload    = JSON.generate({
      event: event_type,
      data:  data })
    webhooks.each do |webhook|
      url = URI.parse(webhook)
      Net::HTTP.start(url.host, url.port) do |http|
        headers       = { 'Content-Type' => 'application/json' }
        response      = http.post('/', payload, headers)
      end
    end
  end
end
...
class Polis < Sinatra::Base
  ...
  post '/' do
    # Note: Thanks to this SO page: http://stackoverflow.com/a/6318491/249218
    http_headers = env.select{|h| h =~ /HTTP_/}
    body_string  = request.body.read
    ...
    logger.info <<-LOG
      Logging POST request:
      Headers:
      #{http_headers}
      Body:
      #{body_string}
    LOG
    status 200
    return body_string
  end
  ...
end
It’s a general practice to
to implement the specifics
but the idea has been around for a while
Lots of flexibility. But you'll have to experiment....
class RandomNumberTexter < Sinatra::Base
  ...
  post '/' do
    request.body.rewind
    post_body = request.body.read
    if request.content_type == 'application/json'
      payload = JSON.parse post_body
      is_random_number_generate = payload['event'] == 'random_number:generate'
      return unless is_random_number_generate
      has_payload_data = payload['data']
      return unless has_payload_data
      random_number = payload['data']
      send_text(random_number)
      status 201
    else
      status 202
    end
    post_body
  end
  ...
end
Loads of companies!
Notes: Companies large and small use webhooks to do more with their service. Even if you don't implement them in your software, it can be nice to understand how other people use them.Several services feed into our #support channel in Slack
GitHub issues and pull requests feed into #github
Continuous integration runs after pushing to GitHub
CI passes/failures show in #engineering
when something needs immediate attention
Sync with CartoDB for visualization and analysis
using PHP or Google Apps Script
to run through custom analysis pipelines
Programmatically add weather data from Forecast.io
Common web patterns make it
plug and play with many tech stacks
and more flexible for our clients
As long as they can use JSON, they're good to go. PHP, Ruby, Java, Node, whatever. They can swap out their entire pipeline if they want, but they can still use webhooks here. That's awesome!They may not care about webhooks at first, but
We don’t have a huge engineering team
Webhooks allow us to have a greater impact