Express.js applications can easily receive PostPenguin webhooks. This guide works for any Node.js backend using Express.
🚀 Quick Setup
1. Install Dependencies
npm install express cors crypto2. Create Webhook Handler
// server.js or routes/webhooks.js
const express = require('express')
const crypto = require('crypto')
const cors = require('cors')
const app = express()
const PORT = process.env.PORT || 3001
// Middleware
app.use(cors())
app.use(express.json())
// PostPenguin webhook endpoint
app.post('/api/webhooks/postpenguin', async (req, res) => {
try {
console.log('📨 Received webhook from PostPenguin')
// Verify webhook signature (recommended)
const signature = req.headers['x-postpenguin-signature']
const webhookSecret = process.env.POSTPENGUIN_WEBHOOK_SECRET
if (webhookSecret && signature) {
const payloadString = JSON.stringify(req.body)
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(payloadString)
.digest('hex')
const receivedSignature = signature.replace('sha256=', '')
if (receivedSignature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' })
}
}
const { title, slug, html, meta_title, meta_description, featured_image } = req.body
// Validate required fields
if (!title || !slug || !html) {
return res.status(400).json({ error: 'Missing required fields: title, slug, html' })
}
// Save to your database
const post = {
id: crypto.randomUUID(),
title,
slug,
html,
metaTitle: meta_title || title,
metaDescription: meta_description || '',
featuredImage: featured_image || '',
publishedAt: new Date().toISOString(),
}
// TODO: Save to your database (PostgreSQL, MongoDB, etc.)
console.log('✅ Post received:', post.title)
res.status(200).json({
success: true,
postId: post.id,
message: 'Post received and saved'
})
} catch (error) {
console.error('❌ Webhook processing error:', error)
res.status(500).json({ error: 'Internal server error' })
}
})
// API endpoint to fetch posts (for your frontend)
app.get('/api/posts', (req, res) => {
try {
const { status = 'publish', limit = 10, offset = 0, slug } = req.query
// TODO: Fetch from your database
let posts = [] // Replace with actual database query
if (slug) {
// Find single post by slug
const post = posts.find(p => p.slug === slug)
if (!post) {
return res.status(404).json({ error: 'Post not found' })
}
return res.json({ post })
}
// Filter and paginate
const filteredPosts = posts.slice(parseInt(offset), parseInt(offset) + parseInt(limit))
res.json({
posts: filteredPosts,
pagination: {
total: posts.length,
limit: parseInt(limit),
offset: parseInt(offset),
has_more: parseInt(offset) + parseInt(limit) < posts.length
}
})
} catch (error) {
console.error('❌ Error fetching posts:', error)
res.status(500).json({ error: 'Internal server error' })
}
})
app.listen(PORT, () => {
console.log(`🚀 Server running on port ${PORT}`)
console.log(`📡 Webhook: http://localhost:${PORT}/api/webhooks/postpenguin`)
console.log(`📖 Posts API: http://localhost:${PORT}/api/posts`)
})
3. Environment Variables
# .env
PORT=3001
POSTPENGUIN_WEBHOOK_SECRET=your-webhook-secret-here4. Configure PostPenguin
When adding your site to PostPenguin:
- Webhook URL:
https://your-domain.com/api/webhooks/postpenguin - Secret Key: Same as
POSTPENGUIN_WEBHOOK_SECRET
💾 Database Integration
See the full Express.js guide in our connections documentation for complete database examples including PostgreSQL, MongoDB, and MySQL.
Need Help?
Read our webhook documentation for technical details, or contact support for custom integrations.