npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pilaniaanand/node-php-bridge

v0.0.1

Published

Bidirectional bridge between Node.js and PHP — call PHP handlers from Node.js and vice versa, framework agnostic.

Readme

node-php-bridge

Bidirectional bridge between Node.js and PHP — call PHP handlers from Node.js and Node.js handlers from PHP. Framework agnostic.

Works with: Express, Next.js, Nuxt.js, Fastify, NestJSLaravel, Symfony, Zend/Laminas, Slim, vanilla PHP.


How It Works

Your Node App                     Your PHP App
(Express / Next / Nuxt)          (Laravel / Symfony / Slim)

   NodeBridge  ◄──── HTTP ────►   PhpBridge
   :5555                           :5556

   register()  ← PHP can call     register() ← Node can call
   call()      → calls PHP         call()     → calls Node

Both sides register named handlers and call each other over local HTTP. JSON payloads. Shared secret auth. Timeout handling. Middleware support.


Installation

npm install node-php-bridge

Quick Start

Node.js side

const NodeBridge = require('node-php-bridge');

const bridge = new NodeBridge({
  port: 5555,          // This Node bridge listens here
  phpPort: 5556,       // PHP bridge listens here
  phpHost: '127.0.0.1',
  secret: 'my-secret', // Must match PHP config
  timeout: 10000,
});

// Register handlers PHP can call
bridge
  .register('pdf.generate', async ({ template, data }) => {
    // use puppeteer, pdfkit, etc.
    return { url: '/storage/output.pdf' };
  })
  .register('email.send', async ({ to, subject, body }) => {
    // use nodemailer, etc.
    return { sent: true };
  });

// Call PHP
const invoice = await bridge.call('invoice.create', {
  customerId: 42,
  items: [{ sku: 'A1', qty: 2 }],
});

// Start bridge server
await bridge.listen();

API

new NodeBridge(config)

| Option | Type | Default | Description | | ---------- | ------ | ----------- | ---------------------------------------------- | | port | number | 5555 | Port this Node bridge listens on | | phpPort | number | 5556 | Port the PHP bridge listens on | | phpHost | string | 127.0.0.1 | PHP bridge host | | secret | string | null | Shared secret for auth (recommended) | | timeout | number | 10000 | Default call timeout in ms | | logLevel | string | 'info' | silent / error / warn / info / debug |

bridge.register(name, fn)

Register a handler PHP can call. fn is async (payload) => result.

bridge.registerAll(handlersMap)

Register multiple handlers at once: bridge.registerAll({ 'a': fn1, 'b': fn2 }).

bridge.unregister(name)

Remove a handler.

bridge.call(handler, payload?, timeout?)

Call a handler on the PHP side. Returns a Promise.

bridge.fire(handler, payload?)

Call PHP without waiting for a response (fire-and-forget).

bridge.use(middleware)

Add middleware: async (handlerName, payload) => void. Throw to reject.

bridge.listen()

Start listening. Returns Promise<NodeBridge>.

bridge.close()

Gracefully stop the server. Returns Promise<void>.

bridge.listHandlers()

Returns string[] of registered handler names.

Events

bridge.on('ready', ({ port }) => { ... });
bridge.on('call', ({ handler, payload, result }) => { ... });
bridge.on('handlerError', (err) => { ... });
bridge.on('error', (err) => { ... });

Framework Examples

Express.js

const express = require('express');
const NodeBridge = require('node-php-bridge');

const app = express();
const bridge = new NodeBridge({ port: 5555, phpPort: 5556 });

bridge.register('pdf.generate', async ({ template, data }) => {
  return { url: `/pdfs/${Date.now()}.pdf` };
});

app.post('/api/invoice', async (req, res) => {
  const invoice = await bridge.call('invoice.create', req.body);
  res.json(invoice);
});

await bridge.listen();
app.listen(3000);

Next.js (pages/api)

// lib/bridge.js — singleton
const NodeBridge = require('node-php-bridge');
let bridge;

export function getBridge() {
  if (!bridge) {
    bridge = new NodeBridge({ port: 5555, phpPort: 5556, secret: process.env.BRIDGE_SECRET });
    bridge.register('pdf.generate', async (p) => { /* ... */ return {}; });
    bridge.listen();
  }
  return bridge;
}

// pages/api/checkout.js
import { getBridge } from '../../lib/bridge';

export default async function handler(req, res) {
  const order = await getBridge().call('order.create', req.body);
  res.json(order);
}

Nuxt 3 (server plugin)

// server/plugins/bridge.js
import NodeBridge from 'node-php-bridge';

export default defineNitroPlugin((nitroApp) => {
  const bridge = new NodeBridge({ port: 5555, phpPort: 5556 });
  bridge.register('render.og', async ({ title }) => { return { url: '...' }; });
  bridge.listen();

  nitroApp.hooks.hook('request', (event) => {
    event.context.bridge = bridge;
  });
});

Security

  • Always set secret in production
  • Bind bridge ports to 127.0.0.1 (localhost only) — never expose to public internet
  • Use Docker internal networks when running in containers

Docker Compose Setup

services:
  node:
    build: ./node-app
    environment:
      PHP_BRIDGE_HOST: php
      BRIDGE_SECRET: ${BRIDGE_SECRET}
    ports:
      - "3000:3000"
      - "5555:5555"

  php:
    build: ./php-app
    environment:
      NODE_BRIDGE_HOST: node
      BRIDGE_SECRET: ${BRIDGE_SECRET}
    ports:
      - "8000:8000"
      - "5556:5556"

Testing

npm test

License

MIT