Skip to main content

How to Build and Deploy a Webhook Service for Streams

Updated on
Sep 6, 2024

Overview

In this guide, we'll show you how to build and deploy a webhook service for receiving data from QuickNode Streams using various programming languages. Whether you're using Node.js, Python, Ruby, PHP, Java, Go, Rust, we've got you covered with step-by-step instructions.

With these examples, you can set up a webhook server in your preferred language, install the necessary dependencies, run the server, and expose it to the internet using ngrok. This will enable you to start receiving data from QuickNode Streams seamlessly.

What You Will Need


Table of Contents

Node.js

Setup Environment and Install Dependencies

mkdir nodejs-webhook
cd nodejs-webhook
npm init -y

Create server.js

const http = require('http');

const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/webhook') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
console.log('Received webhook. Request details:');
console.log('Headers:', JSON.stringify(req.headers, null, 2));

try {
const jsonData = JSON.parse(body);
console.log('Parsed JSON data:');
console.log(JSON.stringify(jsonData, null, 2));
} catch (error) {
console.log('Error parsing JSON:', error.message);
console.log('Raw body:', body);
}

res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Webhook received');
});
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
});

const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

Run the webhook

node server.js

Expose to the Internet

Use ngrok:

ngrok http 3000

Go

Setup Environment and Install Dependencies

mkdir go-webhook
cd go-webhook

Create main.go

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

fmt.Println("Received webhook. Request details:")
headers, _ := json.MarshalIndent(r.Header, "", " ")
fmt.Printf("Headers: %s\n", headers)

body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Error reading body", http.StatusInternalServerError)
return
}

var jsonData interface{}
if err := json.Unmarshal(body, &jsonData); err != nil {
fmt.Println("Error parsing JSON:", err)
fmt.Println("Raw body:", string(body))
} else {
fmt.Println("Parsed JSON data:")
prettyJSON, _ := json.MarshalIndent(jsonData, "", " ")
fmt.Println(string(prettyJSON))
}

w.WriteHeader(http.StatusOK)
w.Write([]byte("Webhook received"))
}

func main() {
http.HandleFunc("/webhook", webhookHandler)
fmt.Println("Server is running on port 3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}

Run the Webhook

go run main.go

Expose to the Internet

Use ngrok:

ngrok http 3000

Python (Flask)

Setup Environment and Install Dependencies

mkdir python-webhook
cd python-webhook
python3 -m venv venv
source venv/bin/activate
pip install flask

Create app.py

from flask import Flask, request, Response
import json

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
print('Received webhook. Request details:')
print('Headers:', json.dumps(dict(request.headers), indent=2))

data = request.get_data()
try:
json_data = json.loads(data)
print('Parsed JSON data:')
print(json.dumps(json_data, indent=2))
except json.JSONDecodeError as e:
print('Error parsing JSON:', str(e))
print('Raw body:', data.decode())

return Response('Webhook received', status=200)

if __name__ == '__main__':
app.run(port=3000)

Run the webhook

node app.py

Expose to the Internet

Use ngrok:

ngrok http 3000

Ruby (Sinatra)

Setup Environment and Install Dependencies

mkdir ruby-webhook
cd ruby-webhook
echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'sinatra'" >> Gemfile
bundle install

Create app.rb

require 'sinatra'
require 'json'

set :port, 3000

post '/webhook' do
puts 'Received webhook. Request details:'
puts "Headers: #{JSON.pretty_generate(headers)}"

request.body.rewind
payload = request.body.read

begin
json_data = JSON.parse(payload)
puts 'Parsed JSON data:'
puts JSON.pretty_generate(json_data)
rescue JSON::ParserError => e
puts "Error parsing JSON: #{e.message}"
puts "Raw body: #{payload}"
end

status 200
body 'Webhook received'
end

Run the Webhook

ruby app.rb -p 3000

Expose to the Internet

Use ngrok:

ngrok http 3000

PHP

Setup Environment and Run Webhook

mkdir php-webhook
cd php-webhook
echo "<?php
$rawData = file_get_contents('php://input');

echo "Received webhook. Request details:\n";
echo "Headers:\n";
echo json_encode(getallheaders(), JSON_PRETTY_PRINT) . "\n";

$jsonData = json_decode($rawData);
if (json_last_error() === JSON_ERROR_NONE) {
echo "Parsed JSON data:\n";
echo json_encode($jsonData, JSON_PRETTY_PRINT) . "\n";
} else {
echo "Error parsing JSON: " . json_last_error_msg() . "\n";
echo "Raw body: $rawData\n";
}

http_response_code(200);
echo 'Webhook received';
?>" > webhook.php
php -S localhost:3000

Expose to the Internet

Use ngrok:

ngrok http 3000

Java

Setup Environment and Install Dependencies

mkdir java-webhook
cd java-webhook

Create WebhookServer.java

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

public class WebhookServer {

public static void main(String[] args) throws IOException {
int port = 3000;
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext("/webhook", new WebhookHandler());
server.setExecutor(null);
server.start();
System.out.println("Server is running on port " + port);
}

static class WebhookHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
if ("POST".equals(exchange.getRequestMethod())) {
System.out.println("Received webhook. Request details:");

// Log headers
System.out.println("Headers:");
exchange.getRequestHeaders().forEach((key, values) ->
System.out.println(key + ": " + String.join(", ", values)));

// Read and log body
InputStream requestBody = exchange.getRequestBody();
byte[] bodyBytes = requestBody.readAllBytes();
String body = new String(bodyBytes, StandardCharsets.UTF_8);

if (body.isEmpty()) {
System.out.println("Body: Empty request received");
} else {
System.out.println("Body:");
System.out.println(body);
}

// Send response
String response = "Webhook received";
exchange.sendResponseHeaders(200, response.length());
try (OutputStream os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
} else {
// Handle non-POST requests
String response = "Method Not Allowed";
exchange.sendResponseHeaders(405, response.length());
try (OutputStream os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
}
}
}
}

Run the Webhook

javac WebhookServer.java
java WebhookServer

Expose to the Internet

Use ngrok:

ngrok http 3000

Rust (Actix-web)

Setup Environment and Install Dependencies

cargo new rust-webhook
cd rust-webhook

Add dependencies to Cargo.toml

[dependencies]
actix-web = "4.0"
serde_json = "1.0"

Create src/main.rs

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use serde_json::Value;

async fn webhook(body: String) -> impl Responder {
println!("Received webhook. Request details:");

match serde_json::from_str::<Value>(&body) {
Ok(json) => {
println!("Parsed JSON data:");
println!("{}", serde_json::to_string_pretty(&json).unwrap());
},
Err(e) => {
println!("Error parsing JSON: {}", e);
println!("Raw body: {}", body);
}
}

HttpResponse::Ok().body("Webhook received")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().route("/webhook", web::post().to(webhook))
})
.bind("127.0.0.1:3000")?
.run()
.await
}

Run the Webhook

cargo run

Expose to the Internet

Use ngrok:

ngrok http 3000

More resources


We ❤️ Feedback!

If you have any feedback or questions about this documentation, let us know. We'd love to hear from you!

Also, check out the QuickNode Forum for more resources. Stay up to date with the latest by following us on Twitter (@QuickNode) or Discord.

Share this doc