29,816 reads
29,816 reads

How to Listen for Webhooks Using Python

by Corentin BrossaultJuly 23rd, 2020
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

Many services, such as SendGrid, Stripe, Slack, and GitHub use events to send webhooks as part of their API. This allows your application to listen for events and perform actions when they happen. In this article, we'll look at how you can listen for Webhooks using Python (v3+) with the Flask or Django frameworks. We establish a route, tell the service where to send data, and our application sits and waits until a request comes in to that route. Django is more automated than Node.js and Express.js.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How to Listen for Webhooks Using Python
Corentin Brossault HackerNoon profile picture

Webhooks run a large portion of the "magic" that happens between applications. They are sometimes called reverse APIs, callbacks, and even notifications. Many services, such as SendGrid, Stripe, Slack, and GitHub use events to send webhooks as part of their API. This allows your application to listen for events and perform actions when they happen.

In a previous article, we looked at how toĀ consume webhooks with Node.js and Express. In this article we'll look at how you can listen for webhooks using Python (v3+) with theĀ FlaskĀ orĀ DjangoĀ frameworks.

This guide assumes you have Python v3 installed on your machine. You can find details on installing python at theĀ official downloads page. Depending on your setup, theĀ 

python
Ā command you want to use may beĀ 
python3
.

You can confirm the installed version by running the following from the terminal:

python --version

or, if that displays a version below 3:

python3 --version

What is a webhook

Webhooks are called reverse APIs for a reason. Instead of your application sending a request to the API, the API sends the request to your application. While the concept may sound different, the way that we consume webhooks is the same way that an API consumes a request.

In most web frameworks, there is the concept of a route. A route allows the application to respond with specific content or data when the user visits a specific URL. The same idea applies to APIs.

When you send a request to GitHub for details about a specific organization, such asĀ 

https://api.github.com/orgs/Bearer
, the route isĀ 
/orgs/:org
Ā whereĀ 
:org
Ā is the name of the organization.

The same concept is applied when receiving webhooks. We establish a route, tell the service where to send the data, and our application sits and waits until a request comes in to that route. There are a few consistencies across webhook implementations.

  1. They are normallyĀ 
    POST
    Ā requests.
  2. They receive JSON data.
  3. They need to respond quickly.

Some APIs will require that your application respond within a certain amount of time, otherwise the event will be re-sent. For example, theĀ Slack APIĀ expects a response back within three seconds.

Receive a webhook with Flask

TheĀ Flask frameworkĀ is a lightweight Python web framework that describes itself as "micro". This allows you to use just what you need, then add more structure later as your project grows.

For our purposes, this is great as we are only concerned with routing. Make sure Python is installed, then run the following command in your terminal to install flask:

python -m pip install Flask

You can find full installation and setup details on at theĀ Flask documentation.

Next, create aĀ 

.py
Ā file, such asĀ 
main.py
Ā and add the following:

from flask import Flask, request, Response

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def respond():
    print(request.json);
    return Response(status=200)

This code imports the Flask class along with the request and Response objects. Then instantiates it with a name ofĀ 

__name__
Ā before assigning it to theĀ 
app
Ā variable. This naming scheme is convention in the Flask documentation.

Next, we use theĀ 

@app.route
Ā decorator to listen forĀ 
POST
Ā requests made against theĀ 
/webhook
Ā path. This decorator calls the function that immediately follows it when a request is made to the route. In this case, that is theĀ 
respond
Ā function.

For the purpose of this example, weĀ 

print
Ā out the request as json, then return aĀ 
Response
Ā with a status code of 200. This response tells the sender that we received the hook. You should be able to run the server using Flask's preferred technique:

export FLASK_APP=main.py
python -m flask run

Thats's it! We now have an app that listens for a webhook with python and flask. Once deployed,Ā 

POST
Ā requests made to the endpoint will trigger theĀ 
respond
Ā function.

For example:Ā 

https://exampledomain.com/webhook.
This is also the URL that you will provide the the service that sends the webhook.

Receive a webhook with Django

Setting up an application in Django is more automated than Flask, but that also comes with a more elaborate file structure. As a more traditional Model-View-Controller (MVC) framework, Django scaffolds out the main parts of the project for you. A full installation guide is available onĀ the official Django documentation page, but it can also be installed withĀ 

pip
Ā using the following command:

python -m pip install Django

If you're setting up a project from scratch, use theĀ 

django-admin
Ā utility to create a new project. If you already have an existing Django project that you want to add webhooks to, skip to the next step.

django-admin startproject example-project

This sets up the basis for a new Django project. Navigate to the newly created folder, and you should see a structure similar to the following:

example-project/
    manage.py
    example-project/
    __init__.py
    settings.py
    urls.py
    asgi.py
    wsgi.py

We can confirm that everything worked by runningĀ 

python manage.py runserver
.

Django's convention is to set up "apps" within this outer "project". You can avoid this and set up a single-app project by runningĀ 

django-admin startproject example-project .
with a trailing period (.) instead. For this tutorial, we'll mirror the preferred way as shown previously.

To do that, we'll set up an "app" calledĀ webhooks.

python manage.py startapp webhooks

This creates a new directory calledĀ webhooks. Great! Now we can write some code.

We'll be focused on three files:

webhooks/views.py
,
webhooks/urls.py 
(not yet created), andĀ 
example-site/urls.py
.

Open the fileĀ 

webhooks/views.py
. Here we will write the logic for handling a route.

from django.http import HttpResponse
from django.views.decorators.http import require_POST

@require_POST
def example(request):
	return HttpResponse('Hello, world. This is the webhook response.')

This code does the following:

  • It imports theĀ 
    HttpResponse
    Ā object that will be used to send a response.
  • It imports a special decorator to limit the request method. In Django, routes accept all HTTP methods by default and let the views manage which methods they respond to.
  • Calls the decorator to limit the function that follows to only theĀ 
    POST
    Ā method.
  • Defines a function, namedĀ 
    example
    Ā that takes the request as an argument and returns a response.

ThisĀ 

example
Ā function's name will be linked to ourĀ 
urls.py
Ā file shortly. It does not need to align to a specific path.

Next, createĀ 

webhooks/urls.py
Ā if it doesn't already exist. This is where we organize routes within this sub-app of our project.

from django.urls import path

from . import views

urlpatterns = [
    path('example/', views.example)
]

Here, we importĀ 

path
Ā fromĀ 
django.urls
. It defines individual routes and associates them with views. We next import all views.

Finally,Ā 

urlpatterns
Ā is passed a list of paths. This list is recognized by Django as the routes associated with the application.

In this instance, we define a path that targetsĀ 

example/
Ā and is associated with the viewĀ 
views.example
, which was the name of our function inĀ 
views.py
.

With this done, our application works, but we need to tell the outer project about it. OpenĀ 

example-project/urls.py
. It should look similar to the previous file, but with an existingĀ 
admin
Ā route. Add a new path like so:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('webhooks/', include('webhooks.urls'))
]

If your server stopped, run it again withĀ 

python manage.py runserver
.

Now try making a POST request to

http://127.0.0.1:8000/webhooks/example/ 
(replace the host and port with your own if they differ).

With that, we have set up a Django project that listens for a webhook atĀ 

/webhooks/example
. Once deployed, append this path to the full URL and provide the full URL to the service that sends the webhook.

Testing webhooks locally

To test webhooks locally without deploying, we need to open up a connection from our development machine to the outside world.

One option is to useĀ ngrok. This service allows you to provide outside access to a specific port on your local machine. This works great for our needs. To get started, sign up and follow the installation and getting started instructions.

Once done, if you're on MacOS, you should be able to runĀ 

./ngrok http 3000
Ā in your terminal where 3000 is replaced with the port of your running Python application. For example, a default Django site often runs on port 8000.

Once up and running, ngrok will provide you with a url that you can use to test your webhook. In Bearer, we provide a "test" button in our Notification settings.

Once configured, you'll start receiving webhooks at that URL. Don't forget to change it over to the final, deployed webhook URL once development and testing are complete.

What can you do with this information?

Once a webhook is configured, it is up to you how to handle the information it receives. You can use this information to react to events in real-time, feature flip parts of your application, or even use it as a way to write data to a database. There are countless capabilities your webhook can have depending on what kind of information the API provider sends.

While we built a basic implementation in this article, it is worth mentioning that many services offer ways to validate that a request is coming from the actual source. This can be done by limiting the URLs that access your application, or by matching a secret key. GitHub, for example, allows you to set a secret that will be sent with each webhook.

Explore the documentation for the services you use to see how best to work within their setup. If you like the idea of using webhooks to react to monitoring changes with your third-party API providers,Ā check out what we're building at Bearer.sh.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks