logo

Are you need IT Support Engineer? Free Consultant

Node.js based HTTP Web/Debug Monitor for SAP BTP

  • By Sanjay
  • 02/06/2026
  • 22 Views


When working on integration and extensibility projects, it’s often handy to be able to “peek into messages.” Recently, I worked with SAP Sales/Service Cloud Version 2 webhooks and wanted to see real messages, transmitted headers, etc., before implementing my webhook logic.

In the past, I’ve used Integration Flows in Integration Suite for this kind of thing, but it felt a bit cumbersome.

Lately, I had the idea of deploying a Node.js app on SAP BTP for this purpose, which provides a debug endpoint for receiving any kind of messages and a small web UI that displays received messages live (as long as you keep the page open in the browser) using SSE (server-sent-events).

WARNING: If the source code shown here is used without modifications (especially without XSUAA), the debug and web UI endpoints are openly accessible on the internet without authentication. Furthermore, the monitor also displays confidential details such as the Authorization header.

Felix_Wyskocil_1-1780349338463.Png

 

I confess, I've created the main part of the debug monitor with Claude Code 😜 and adapted it further to my needs.
You can find the required files in the following source code snippets. The server.js also includes the stub of a sample webhook that logs the request and after processing also the prepared response to the monitor.

Feel free to use the code and adapt to your needs.

Files

  • public/index.html
  • manifest.yml
  • package.json
  • server.js

 

manifest.yml

---
applications:
  - name: debug-monitor
    memory: 64M
    disk_quota: 512M
    instances: 1
    buildpacks:
      - nodejs_buildpack
    health-check-type: http
    health-check-http-endpoint: /health

 

package.json

{
  "type": "module",
  "name": "debug-monitor",
  "version": "1.0.0",
  "description": "HTTP Debug Monitor",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.21.2"
  },
  "engines": {
    "node": "24"
  }
}

 

server.js

import express from 'express'
import { fileURLToPath } from 'url'
import path from 'path'

const PORT = process.env.PORT || 3000

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const app = express()
app.use(express.json({ limit: '5mb' }))
app.use("https://community.sap.com/", express.static(path.join(__dirname, 'public')))

// SSE Clients
const sseClients = new Set()

/**
 * Send a record to the debug monitor
 */
function pushEntryToDebugMonitor(entry)
{
  if (sseClients.size === 0)
      return;

  const payload = JSON.stringify({
     id: crypto.randomUUID(),
     timestamp: new Date().toISOString(),
     ...entry 
  });

  sseClients.forEach(client => client.write(`data: ${payload}\n\n`))
}

/**
 * Prepare a debug record and send it to the monitor
 * @Param {*} source - caption in the monitor
 * @Param {*} req - request object
 * @Param {*} resBody - response body (if available)
 * @Param {*} isFollowUp - just a flag for the monitor for displaying -> e.g. when sending the request body before processing and the request+response after processing to the debug monitor
 */
function pushToDebugMonitor(source, req, resBody = null, isFollowUp = false)
{
  const entry = {
    source: source,
    method: req.method,
    url: req.originalUrl,
    httpVersion: req.httpVersion,
    query: req.query,
    headers: req.headers,
    requestBody: req.body,
    responseBody: resBody,
    isFollowUp: isFollowUp
  };

  pushEntryToDebugMonitor(entry);
}

/**
 * General debug endpoint to send data to the debug monitor
 */
app.all(['/debug', '/debug/*'], (req, res) => {
  pushToDebugMonitor('Debug Endpoint', req);
  res.status(200).json({ ok: true })
})

/**
 * Debug monitor: SSE stream for the UI
 */
app.get('/debug-events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders();
  sseClients.add(res);
  req.on('close', () => sseClients.delete(res));
})

/**
 * Health check endpoint
 */
app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    service: 'HTTP Debug Monitor',
    timestamp: new Date().toISOString()
  })
})

/**
 * Sample Webhook
 */
app.post('/webhooks/visit/posthook', async (req, res) => {

  try
  {
    // add the request to the debug monitor
    pushToDebugMonitor('Visit Posthook Req.', req);

    // process the request ...

    // add the request + response to the debug monitor (responseBody was created during processing)
    pushToDebugMonitor('Visit Posthook Resp.', req, responseBody, true);
  }
  catch (error)
  {
    console.error('❌ Error in webhook-visit-post:', error)
    res.status(500).json({ error: 'Internal server error' })
  }
});

app.listen(PORT, () => {
  console.log(`🛜 Listening on port ${PORT}`);
});

 

index.html

Don't forget that the server.js delivers static files from the public folder. Hence, the index.html must be placed in the subfolder “public”.




  
  
  Debug Monitor
  





Source link

Leave a Reply

Your email address will not be published. Required fields are marked *