Skip to content

Latest commit

 

History

History
303 lines (222 loc) · 7.16 KB

File metadata and controls

303 lines (222 loc) · 7.16 KB

Volley Local Development Guide

Complete guide to testing webhooks locally with Volley - Works with any framework, any language, any webhook provider.

Volley License: MIT

This repository demonstrates how to test webhooks locally using Volley with various frameworks and languages. No ngrok, no tunneling, no exposing your server.

🎯 What You'll Learn

  • How to set up Volley for local webhook testing
  • How to use Volley CLI to forward webhooks
  • Examples in multiple languages and frameworks
  • Best practices for local webhook development

🚀 Quick Start

Prerequisites

Installation

  1. Install Volley CLI:

    # macOS
    brew tap volleyhq/volley
    brew install volley
    
    # Linux
    wget https://github.com/volleyhq/volley-cli/releases/latest/download/volley-linux-amd64.tar.gz
    tar -xzf volley-linux-amd64.tar.gz
    sudo mv volley /usr/local/bin/
  2. Login to Volley:

    volley login
  3. Create a webhook source:

    • Go to Volley Dashboard
    • Create a new source
    • Copy your ingestion ID (e.g., abc123xyz)
  4. Configure your webhook provider:

    • Add your Volley webhook URL: https://api.volleyhooks.com/hook/YOUR_INGESTION_ID
    • Configure in Stripe, GitHub, Twilio, etc.
  5. Start your local server (see examples below)

  6. Forward webhooks to localhost:

    volley listen --source YOUR_INGESTION_ID --forward-to http://localhost:3000/webhook

📚 Examples

Node.js / Express

// server.js
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
  console.log('Webhook received:', req.body);
  // Your webhook handling logic here
  res.json({ received: true });
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Forward webhooks:

volley listen --source abc123xyz --forward-to http://localhost:3000/webhook

Python / Flask

# app.py
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.json
    print(f'Webhook received: {data}')
    # Your webhook handling logic here
    return jsonify({'received': True})

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

Forward webhooks:

volley listen --source abc123xyz --forward-to http://localhost:3000/webhook

Python / FastAPI

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class WebhookData(BaseModel):
    event: str
    data: dict

@app.post("/webhook")
async def webhook(data: WebhookData):
    print(f'Webhook received: {data}')
    # Your webhook handling logic here
    return {"received": True}

Run:

uvicorn main:app --port 3000
volley listen --source abc123xyz --forward-to http://localhost:3000/webhook

Go

// main.go
package main

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

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    var data map[string]interface{}
    json.NewDecoder(r.Body).Decode(&data)
    
    fmt.Printf("Webhook received: %+v\n", data)
    // Your webhook handling logic here
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]bool{"received": true})
}

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

Run:

go run main.go
volley listen --source abc123xyz --forward-to http://localhost:3000/webhook

Ruby / Sinatra

# app.rb
require 'sinatra'
require 'json'

post '/webhook' do
  data = JSON.parse(request.body.read)
  puts "Webhook received: #{data}"
  # Your webhook handling logic here
  { received: true }.to_json
end

Run:

ruby app.rb
volley listen --source abc123xyz --forward-to http://localhost:3000/webhook

🔄 Multiple Destinations

You can forward the same webhook source to multiple local endpoints:

# Terminal 1: Main API
volley listen --source abc123xyz --forward-to http://localhost:3000/webhook

# Terminal 2: Webhook processor
volley listen --source abc123xyz --forward-to http://localhost:3001/process

# Terminal 3: Logging service
volley listen --source abc123xyz --forward-to http://localhost:3002/log

🆚 Why Volley Instead of ngrok?

Feature Volley ngrok
Webhook URLs ✅ Permanent, never change ❌ Change on restart
Tunneling ❌ Not required ✅ Required
Local Server Privacy ✅ Completely private ⚠️ Exposed through tunnel
Built-in Retry ✅ Automatic ❌ No
Monitoring ✅ Full dashboard ❌ Limited
Production Ready ✅ Same URL for dev/prod ❌ Dev tool only
Offline Support ✅ Webhooks queued ❌ Must be online

Learn more: Volley vs ngrok

🔐 Security Best Practices

1. Verify Webhook Signatures

Always verify webhook signatures when available:

// Example: Stripe webhook verification
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const event = stripe.webhooks.constructEvent(
  req.body,
  req.headers['stripe-signature'],
  process.env.STRIPE_WEBHOOK_SECRET
);

2. Use Environment Variables

Never commit secrets:

# .env
WEBHOOK_SECRET=your_secret_here

3. Idempotency

Handle duplicate webhooks:

const eventId = req.body.id;
if (await isEventProcessed(eventId)) {
  return; // Already processed
}
await markEventAsProcessed(eventId);

🧪 Testing

Manual Testing

  1. Trigger a webhook from your provider (Stripe, GitHub, etc.)
  2. Watch it appear in your local server logs
  3. Check Volley dashboard for delivery status

Automated Testing

// test/webhook.test.js
const request = require('supertest');
const app = require('../server');

test('handles webhook', async () => {
  const response = await request(app)
    .post('/webhook')
    .send({ event: 'test', data: {} });
  
  expect(response.status).toBe(200);
});

📚 Additional Resources

🤝 Contributing

Found a bug or want to add an example? Please open an issue or submit a pull request!

📄 License

MIT License - See LICENSE file for details


Built with ❤️ using Volley