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

laravel-queue-bridge

v1.0.0

Published

Serialize and deserialize Laravel Queue messages (PHP serialized) in Node.js/TypeScript

Readme

laravel-queue-bridge

Bridge between Laravel Queue and Node.js / TypeScript.

Laravel Queue publishes jobs to RabbitMQ as a JSON envelope wrapping a PHP serialize() string. This zero-dependency library lets Node.js services work with that format in both directions:

  • Consumer — deserialize incoming Laravel Queue messages into plain JS objects
  • Producer — serialize and create messages that Laravel workers can unserialize() natively

Install

npm install laravel-queue-bridge

AI assistant integration (optional)

Install docs for your AI coding assistant so it understands how to use this library:

npx laravel-queue-bridge install          # all supported AI tools
npx laravel-queue-bridge install claude    # Claude Code only
npx laravel-queue-bridge install copilot   # GitHub Copilot only
npx laravel-queue-bridge install cursor    # Cursor only
npx laravel-queue-bridge install codex     # OpenAI Codex only
npx laravel-queue-bridge install windsurf  # Windsurf only

| AI Tool | Files installed | |---------|----------------| | Claude Code | .claude/skills/ + .claude/docs/ | | GitHub Copilot | .github/copilot-instructions.md | | Cursor | .cursor/rules/ | | OpenAI Codex | AGENTS.md | | Windsurf | .windsurfrules |

Re-run after upgrading the package to get the latest docs.


Use case 1: NestJS as Consumer (receive from Laravel)

Laravel dispatches a job to RabbitMQ. Your NestJS service consumes the queue and processes it.

Laravel side (Producer)

// App\Jobs\UserChangedJob
class UserChangedJob implements ShouldQueue
{
    private string $type;        // "create" | "update" | "delete"
    public $payload;             // user data array or userId (int)

    public function __construct(string $type, $payload)
    {
        $this->type = $type;
        $this->payload = $payload;
    }
}

// Dispatch
dispatch(new UserChangedJob('create', [
    'id'    => 100,
    'name'  => 'Nguyen Van A',
    'email' => '[email protected]',
]))->onQueue('contact');

Laravel serializes this into a RabbitMQ message:

{
  "uuid": "a1b2c3d4-...",
  "job": "Illuminate\\Queue\\CallQueuedHandler@call",
  "data": {
    "commandName": "App\\Jobs\\UserChangedJob",
    "command": "<PHP serialized string — contains private/protected visibility prefixes>"
  },
  "attempts": 0
}

The command field contains a PHP serialize() output like:

O:23:"App\Jobs\UserChangedJob":2:{
  s:29:"<\0>App\Jobs\UserChangedJob<\0>type";s:6:"create";
  s:7:"payload";a:3:{s:2:"id";i:100;s:4:"name";s:12:"Nguyen Van A";...}
}

<\0> = null byte. Private properties use \0ClassName\0key, protected use \0*\0key.

NestJS side (Consumer)

import { parseLaravelQueueMessage } from 'laravel-queue-bridge';

// Inside your RabbitMQ consumer
channel.consume('contact', (msg) => {
  const { jobName, shortName, properties } = parseLaravelQueueMessage(msg.content);

  console.log(jobName);    // "App\\Jobs\\UserChangedJob"
  console.log(shortName);  // "UserChangedJob"
  console.log(properties); // { type: "create", payload: { id: 100, name: "Nguyen Van A", ... } }

  // The library automatically:
  // - Parses the JSON envelope
  // - Deserializes the PHP serialize() string inside data.command
  // - Strips PHP visibility prefixes (\0ClassName\0 for private, \0*\0 for protected)
  // - Returns clean JS object with plain property names

  channel.ack(msg);
});

With TypeScript generics

interface UserChangedProps {
  type: 'create' | 'update' | 'delete';
  payload: { id: number; name: string; email: string } | number;
}

const msg = parseLaravelQueueMessage<UserChangedProps>(raw);
msg.properties.type;           // 'create' | 'update' | 'delete'
msg.properties.payload;        // typed payload

Use case 2: NestJS as Producer (send to Laravel)

Your NestJS service publishes a message. A Laravel worker consumes it and runs the job's handle() method.

NestJS side (Producer)

import { createLaravelQueueMessage } from 'laravel-queue-bridge';

const message = createLaravelQueueMessage({
  jobName: 'App\\Jobs\\CompanyChangedJob',
  properties: {
    action: 'create',
    payload: { id: 1, name: 'ABC Corporation', tel: '03-1234-5678' },
  },
  // Must match the PHP class property visibility exactly
  visibility: {
    action: 'private',     // private $action   in PHP
    payload: 'protected',  // protected $payload in PHP
  },
});

channel.publish(exchange, 'tnet', Buffer.from(message));

The library produces this message:

{
  "uuid": "auto-generated",
  "job": "Illuminate\\Queue\\CallQueuedHandler@call",
  "data": {
    "commandName": "App\\Jobs\\CompanyChangedJob",
    "command": "<PHP serialized object with visibility prefixes>"
  },
  "attempts": 0
}

The generated command contains proper PHP visibility prefixes:

O:26:"App\Jobs\CompanyChangedJob":2:{
  s:34:"<\0>App\Jobs\CompanyChangedJob<\0>action";s:6:"create";   ← private
  s:10:"<\0>*<\0>payload";a:3:{s:2:"id";i:1;s:4:"name";...}      ← protected
}

Laravel side (Consumer) — what the worker receives

// App\Jobs\CompanyChangedJob
class CompanyChangedJob implements ShouldQueue
{
    private string $action;      // ← visibility MUST match
    protected $payload;          // ← visibility MUST match

    public function __construct(string $action, $payload)
    {
        $this->action = $action;
        $this->payload = $payload;
    }

    public function handle(): void
    {
        // Laravel unserialize() maps the message directly to this class instance.
        // $this->action  === "create"
        // $this->payload === ["id" => 1, "name" => "ABC Corporation", "tel" => "03-1234-5678"]

        match ($this->action) {
            'create' => $this->onCreate($this->payload),
            'update' => $this->onUpdate($this->payload),
            'delete' => $this->onDelete($this->payload),
        };
    }
}

Laravel's queue worker calls unserialize() on data.command, which reconstructs the CompanyChangedJob instance with $action = "create" and $payload = [...] — then calls handle().

Why visibility matters

PHP serialize() encodes property visibility into the key using null-byte prefixes:

| Visibility | Serialized key | Example | |------------|---------------|---------| | public | propName | s:6:"action"; | | protected | \0*\0propName | s:10:"\0*\0payload"; | | private | \0ClassName\0propName | s:34:"\0App\Jobs\CompanyChangedJob\0action"; |

If visibility doesn't match, unserialize() will create the object but the properties will be null because PHP can't map the keys to the correct class properties.

More examples

// Job with all public properties — no visibility needed (default)
createLaravelQueueMessage({
  jobName: 'App\\Jobs\\CompanyBatchUpdatedJob',
  properties: { payload: [{ id: 1, name: 'Updated' }] },
});

// Job with protected property
createLaravelQueueMessage({
  jobName: 'App\\Jobs\\UserServiceActivationChangeJob',
  properties: { payload: { userId: 100, serviceCode: 'contact', type: 'add' } },
  visibility: { payload: 'protected' },
});

// Forward slashes work too (auto-converted to backslashes)
createLaravelQueueMessage({
  jobName: 'App/Jobs/UserChangedJob',
  properties: { type: 'delete', payload: 100 },
  visibility: { type: 'private' },
});

API Reference

parseLaravelQueueMessage<T>(raw)

Parse a raw Laravel Queue message into a typed object.

| Parameter | Type | Description | |-----------|------|-------------| | raw | Buffer \| string | Raw message from the queue broker |

Returns LaravelQueueMessage<T>

interface LaravelQueueMessage<T = Record<string, any>> {
  uuid: string;        // Job UUID assigned by Laravel
  jobName: string;     // "App\\Jobs\\UserChangedJob"
  shortName: string;   // "UserChangedJob"
  properties: T;       // Deserialized job properties (visibility prefixes stripped)
  attempts: number;    // Number of retry attempts
}

Throws PhpUnserializeError on malformed input.


createLaravelQueueMessage(options)

Create a Laravel Queue compatible JSON string ready to publish.

interface CreateLaravelQueueMessageOptions {
  jobName: string;                                        // PHP FQCN (accepts / or \\)
  properties: Record<string, any>;                        // Job properties
  visibility?: PhpVisibility | Record<string, PhpVisibility>;  // Default: 'public'
  uuid?: string;                                          // Auto-generated if omitted
  attempts?: number;                                      // Default: 0
}

type PhpVisibility = 'public' | 'protected' | 'private';

Returns string — JSON string.


phpUnserialize(serialized)

Low-level: deserialize a PHP serialize() string into a JavaScript value.

phpUnserialize('s:5:"hello";');                          // "hello"
phpUnserialize('i:42;');                                 // 42
phpUnserialize('a:2:{s:2:"id";i:1;s:4:"name";s:5:"Alice";}'); // { id: 1, name: "Alice" }

| PHP type | JS result | |----------|-----------| | s (string) | string | | i (integer) | number | | d (double) | number | | b (boolean) | boolean | | N (null) | null | | a (array) | Record<string, any> | | O (object) | Record<string, any> (class name discarded, visibility stripped) |

Handles: UTF-8 multi-byte strings, INF / -INF / NAN, nested structures, visibility prefixes.


phpSerialize(value)

Low-level: serialize a JavaScript value into a PHP serialize() compatible string.

phpSerialize('hello');         // 's:5:"hello";'
phpSerialize(42);              // 'i:42;'
phpSerialize({ id: 1 });      // 'a:1:{s:2:"id";i:1;}'
phpSerialize([10, 20]);       // 'a:2:{i:0;i:10;i:1;i:20;}'
phpSerialize('Hello 世界');    // 's:12:"Hello 世界";'  (byte length, not char length)

phpSerializeObject(className, properties, options?)

Low-level: serialize as a PHP object (O:) with class name and optional visibility.

phpSerializeObject('App\\Jobs\\MyJob', { action: 'test' }, {
  visibility: { action: 'private' },
});
// O:15:"App\Jobs\MyJob":1:{s:23:"\0App\Jobs\MyJob\0action";s:4:"test";}

PhpUnserializeError

Custom error thrown when deserialization fails.

try {
  phpUnserialize('INVALID');
} catch (err) {
  if (err instanceof PhpUnserializeError) {
    err.message;   // Human-readable error with position
    err.position;  // Character offset where parsing failed
    err.source;    // The original serialized string
  }
}

Exports

| Export | Direction | Description | |--------|-----------|-------------| | parseLaravelQueueMessage | Laravel → Node.js | Parse Laravel Queue message | | createLaravelQueueMessage | Node.js → Laravel | Create Laravel Queue message | | phpUnserialize | PHP → JS | Deserialize PHP string | | phpSerialize | JS → PHP | Serialize to PHP string | | phpSerializeObject | JS → PHP | Serialize as PHP object with visibility | | PhpUnserializeError | — | Error class for parse failures | | LaravelQueueMessage | — | TypeScript interface (parse result) | | LaravelQueueEnvelope | — | TypeScript interface (raw JSON envelope) | | CreateLaravelQueueMessageOptions | — | TypeScript interface (create options) | | PhpVisibility | — | 'public' \| 'protected' \| 'private' | | SerializeObjectOptions | — | TypeScript interface (visibility options) |

Requirements

  • Node.js >= 14
  • Zero runtime dependencies

License

MIT