Intro to Webhooks with Python
Tags: programming, tutorial, python, webdev
Published: 2021-10-26 06:21:28

This tutorial will be an introduction to the concept of webhooks. We will also build a simple Flask server that can receive GitHub webhooks. We will also see how to expose our local hosts. 

### What is a webhook? 
Before talking about webhooks, let's talk about APIs. Below is the data flow for an API.
 
![API Data Flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8s1cujrspgu6tewi3lh.png)

You make a GET/POST request to the API and you get a response back. If you want to learn more about working with APIs, check out my article on working with [APIs with Python](https://www.realpythonproject.com/how-to-authenticate-using-keys-basicauth-oauth-in-python/) or my article on working with [APIs with JavaScript](https://www.realpythonproject.com/how-to-authenticate-using-keys-basicauth-oauth2-in-javascript/).

Consider the Github API, what if we wanted to build an API that would send use an email every time a new issue is made in a repo. One way would be to build an API that would make a request every 1-2 minutes to check if a new issue is made and notify us. This process is known as [polling](https://tyk.io/blog/moving-beyond-polling-to-async-apis/#:~:text=APIs%20commonly%20require%20a%20client,requests%20data%20from%20the%20server.&text=This%20pattern%20is%20known%20as,or%20notified%20of%20backend%20events.). Basically we would have to periodically make requests to check for a new issue. However, this seems inefficient. What if GitHub makes a request to our API whenever a new issue is created. This is known as a webhook. Instead of us making periodic requests, we just give GitHub our API's endpoint and whenever a new issue is created, a request will be made to the endpoint we gave to Github. Webhooks are also known as Reverse APIs. Below is a comparison between polling and webhooks. This image was inspired by [this article](https://www.mailjet.com/blog/news/what-is-webhook/)

![Polling vs WebHooks](https://cdn.hashnode.com/res/hashnode/image/upload/v1635228126977/UzxuZmJIr.png)

As you might have noticed, a lot of requests are made and depending on how frequently we make requests, there might be a slight delay between the new issue being created and our API getting notified.
 
Let's create an API to receive a request whenever a new Issue is created in a Github repo.

### Create a simple Flask Server
First, let's create a hello world endpoint.

{%gist https://gist.github.com/rahulbanerjee26/617d073669e78b538fabccfe3219515c %}

Now we need to create an endpoint to receive the request from the GitHub API. This will be a standard endpoint that accepts POST requests.

{%gist https://gist.github.com/rahulbanerjee26/4de2f9e12331616f63f078bcf9d064bd %}

I read the docs and knew the keys for the JSON objects. You can use different keys to access more data like issue labels etc.

Now you can run your flask server
```bash
python3 __init__.py
```
### Exposing our localhost URL publically
Webhooks require a public API endpoint since they can not make requests to endpoints such as 'http://127.0.0.1:5000/'. One way would be to deploy the API and use the URL of the deployed API. Another way is to expose your localhost as a publically available URL. This will be temporary and will only work as long as your Flask server is running. Any request made to the public URL will also be made to your localhost URL.

We will use [ngrok](https://ngrok.com/) to expose our localhost URL. You will have to create an account.

![Screen Shot 2021-10-26 at 1.41.52 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1635226921573/lM5MkEfJ8.png)
Download ngrok for your OS and unzip it. Now open a terminal and cd to the directory where the unzipped ngrok file is present. Type the following command in the terminal
```bash
ngrok  http <PORT NUMBER> 
```
Eg: If your flask server is running on port 5000, you'd have to type the following
```bash
ngrok http 5000
```

![Screen Shot 2021-10-26 at 1.38.25 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1635226711318/if00V8gy05.png)
 You should see a similar output in your terminal. The public URL is the URL next to 'Forwarding'. In my case it is http://35cc-69-58-102-156.ngrok.io. If you access your public URL, you should see the same thing you see when you access your localhost URL. 

### Creating an Issue Webhook
Chose any Github Repo you like. Go to Settings> Webhooks > Add Webhook
![Screen Shot 2021-10-26 at 1.40.56 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1635226862829/AK1RV-qTX.png)


![Screen Shot 2021-10-26 at 1.43.52 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1635227038512/Hg_4vQZrP1.png)

Enter your endpoint, in my case it is http://35cc-69-58-102-156.ngrok.io/githubIssue

Since we only want requests to be made when an Issue is created in the repo, select 'Let me select individual events.' and scroll down to choose 'Issues'. Once you are down, scroll down and create the webhook.

### Testing the Webhook
Create an Issue in your repo

![Screen Shot 2021-10-26 at 1.47.09 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1635227234544/NHo5VaJO8.png)

Now check the terminal where the flask server is running. I also added a label and closed the issue. 


![Screen Shot 2021-10-26 at 1.48.43 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1635227338152/z6CVghu0H.png)

### Conclusion
Right now the Flask server doesn't do much. However, you can build on top of it. Instead of just printing the data received, you can use the data and send a push notification or an email. I hope you found this article helpful. Connect with on [LinkedIn](https://www.linkedin.com/in/rahulbanerjee2699/) , [Twitter](https://twitter.com/rahulbanerjee99)