Agent skill
glide-mq-migrate-bee
Migrates Node.js applications from Bee-Queue to glide-mq. Covers the chained builder-to-options API conversion, Queue/Worker separation, and event mapping. Use when converting bee-queue projects to glide-mq, replacing bee-queue with glide-mq, or planning a bee-queue migration. Triggers on "bee-queue to glide-mq", "replace bee-queue with glide-mq", "migrate from bee-queue", "beequeue migration glide-mq".
Install this agent skill to your Project
npx add-skill https://github.com/avifenesh/glide-mq/tree/main/skills/glide-mq-migrate-bee
Metadata
Additional technical details for this skill
- tags
- bee-queue, migration, glide-mq, valkey, redis, job-queue
- author
- glide-mq
- sources
- docs/USAGE.md
- version
- 0.14.0
SKILL.md
Migrate from Bee-Queue to glide-mq
When to Apply
Use this skill when:
- Replacing bee-queue with glide-mq in an existing project
- Converting Bee-Queue's chained job API to glide-mq's options API
- Updating connection configuration from ioredis to valkey-glide
- Upgrading from bee-queue due to Node.js compatibility or maintenance issues
Step-by-step guide for converting Bee-Queue projects to glide-mq. Bee-Queue uses a chained job builder pattern - this migration requires rewriting job creation and separating producer/consumer concerns.
Why Migrate
- Unmaintained - last release 2021, accumulating Node.js compatibility issues
- No cluster support - cannot scale beyond a single Redis instance
- No TLS - requires manual ioredis workarounds for encrypted connections
- No native TypeScript - community
@types/bee-queueonly, often outdated - No priority queues - workaround is multiple queues
- No workflows - no parent-child jobs, no DAGs, no repeatable/cron jobs
- No rate limiting, batch processing, or broadcast
- glide-mq provides all Bee-Queue features plus 35%+ higher throughput
Breaking Changes Summary
| Feature | Bee-Queue | glide-mq |
|---|---|---|
| Queue + Worker | Single Queue class |
Separate Queue (producer) and Worker (consumer) |
| Job creation | queue.createJob(data).save() (chained) |
queue.add(name, data, opts) (single call) |
| Job name | Not used - no name parameter | Required first argument to queue.add() |
| Job options | Chained: .timeout(ms).retries(n) |
Options object: { attempts, backoff, delay } |
| Retries | .retries(n) |
{ attempts: n } (different name!) |
| Processing | queue.process(concurrency, handler) |
new Worker(name, handler, { concurrency }) |
| Connection | { host, port } or redis URL |
{ addresses: [{ host, port }] } |
| Progress | job.reportProgress(anyJSON) |
job.updateProgress(number | object) (number 0-100 or object) |
| Per-job events | job.on('succeeded', ...) |
QueueEvents class (centralized) |
| Stall detection | Manual checkStalledJobs() |
Automatic on Worker |
| Batch save | queue.saveAll(jobs) |
queue.addBulk(jobs) |
| Producer-only | { isWorker: false } |
Producer class or just Queue |
Queue Settings Mapping
| Bee-Queue Setting | Default | glide-mq Equivalent | Notes |
|---|---|---|---|
redis |
{} |
connection: { addresses: [...] } |
Array of { host, port } objects |
isWorker |
true |
Use Producer or Queue class |
Separate classes replace flag |
getEvents |
true |
Use QueueEvents class |
Separate class for event subscription |
sendEvents |
true |
events: true on Worker |
Controls lifecycle event emission |
storeJobs |
true |
Always true | glide-mq always stores jobs |
ensureScripts |
true |
Automatic | Server Functions loaded automatically |
activateDelayedJobs |
false |
Automatic | Server-side delayed job activation |
removeOnSuccess |
false |
{ removeOnComplete: true } |
Per-job option on queue.add() |
removeOnFailure |
false |
{ removeOnFail: true } |
Per-job option on queue.add() |
stallInterval |
5000 |
lockDuration on Worker |
Lock-based stall detection |
nearTermWindow |
20min |
N/A | Valkey-native delayed processing |
delayedDebounce |
1000 |
N/A | Server-side scheduling |
prefix |
'bq' |
prefix on Queue |
Default: 'glide' |
quitCommandClient |
true |
Automatic | Handled by graceful shutdown |
redisScanCount |
100 |
N/A | Different key strategy |
Queue Method Mapping
| Bee-Queue Method | glide-mq Equivalent | Notes |
|---|---|---|
queue.createJob(data) |
queue.add(name, data, opts) |
Name is required; returns Job not builder |
queue.process(n, handler) |
new Worker(name, handler, { concurrency: n }) |
Separate class |
queue.checkStalledJobs(interval) |
Automatic on Worker | No manual call needed |
queue.checkHealth() |
queue.getJobCounts() |
Returns { waiting, active, completed, failed, delayed } |
queue.close() |
gracefulShutdown([...]) |
Or individual .close() calls |
queue.ready() |
worker.waitUntilReady() |
On Worker, not Queue |
queue.isRunning() |
worker.isRunning() |
On Worker |
queue.getJob(id) |
queue.getJob(id) |
Same API |
queue.getJobs(type, page) |
queue.getJobs(type, start, end) |
Range-based pagination |
queue.removeJob(id) |
(await queue.getJob(id)).remove() |
Via Job instance |
queue.saveAll(jobs) |
queue.addBulk(jobs) |
Different input format |
queue.destroy() |
queue.obliterate() |
Removes all queue data |
Event Mapping
| Bee-Queue Event | Source | glide-mq Equivalent | Source |
|---|---|---|---|
queue.on('ready') |
Queue | worker.waitUntilReady() |
Worker |
queue.on('error', err) |
Queue | worker.on('error', err) |
Worker |
queue.on('succeeded', job, result) |
Queue (local) | worker.on('completed', job) |
Worker |
queue.on('retrying', job, err) |
Queue (local) | worker.on('failed', job, err) |
Worker (with retries remaining) |
queue.on('failed', job, err) |
Queue (local) | worker.on('failed', job, err) |
Worker |
queue.on('stalled', jobId) |
Queue | worker.on('stalled', jobId) |
Worker |
queue.on('job succeeded', id, result) |
Queue (PubSub) | events.on('completed', { jobId }) |
QueueEvents |
queue.on('job failed', id, err) |
Queue (PubSub) | events.on('failed', { jobId }) |
QueueEvents |
queue.on('job retrying', id, err) |
Queue (PubSub) | No direct equivalent | Use events.on('failed') + retry check |
queue.on('job progress', id, data) |
Queue (PubSub) | events.on('progress', { jobId, data }) |
QueueEvents |
job.on('succeeded', result) |
Job | events.on('completed', { jobId }) |
QueueEvents (filter by jobId) |
job.on('failed', err) |
Job | events.on('failed', { jobId }) |
QueueEvents (filter by jobId) |
job.on('progress', data) |
Job | events.on('progress', { jobId }) |
QueueEvents (filter by jobId) |
Per-job events (job.on(...)) do not exist in glide-mq. Use QueueEvents and filter by jobId, or use queue.addAndWait() for request-reply patterns.
Step-by-Step Conversion
1. Connection
// BEFORE (Bee-Queue)
const Queue = require('bee-queue');
const queue = new Queue('tasks', {
redis: { host: 'localhost', port: 6379 }
});
// AFTER (glide-mq)
import { Queue, Worker } from 'glide-mq';
const connection = { addresses: [{ host: 'localhost', port: 6379 }] };
const queue = new Queue('tasks', { connection });
2. Job Creation (Biggest Change)
Bee-Queue uses chained builder with no job name. glide-mq uses a single call with a required name.
// BEFORE (Bee-Queue) - chained builder, no name
const job = await queue.createJob({ email: 'user@example.com' })
.retries(3)
.backoff('exponential', 1000)
.delayUntil(Date.now() + 60000)
.setId('unique-123')
.save();
// AFTER (glide-mq) - options object, name required
await queue.add('send-email',
{ email: 'user@example.com' },
{
attempts: 3, // NOT "retries" - different name!
backoff: { type: 'exponential', delay: 1000 },
delay: 60000,
jobId: 'unique-123',
}
);
3. Worker
// BEFORE (Bee-Queue)
queue.process(10, async (job) => {
return { processed: true };
});
queue.on('succeeded', (job, result) => console.log('Done:', result));
// AFTER (glide-mq) - separate Worker class
const worker = new Worker('tasks', async (job) => {
return { processed: true };
}, { connection, concurrency: 10 });
worker.on('completed', (job) => console.log('Done:', job.returnValue));
4. Batch Save
// BEFORE (Bee-Queue)
const jobs = items.map(item => queue.createJob(item));
await queue.saveAll(jobs);
// AFTER (glide-mq) - each entry needs a name
await queue.addBulk(items.map(item => ({
name: 'process',
data: item
})));
5. Producer-Only
// BEFORE (Bee-Queue) - disable worker mode
const queue = new Queue('tasks', {
isWorker: false, getEvents: false, sendEvents: false,
redis: { host: 'localhost', port: 6379 }
});
// AFTER (glide-mq) - Producer class
import { Producer } from 'glide-mq';
const producer = new Producer('tasks', { connection });
await producer.add('job-name', data);
await producer.close();
6. Progress Reporting
// BEFORE (Bee-Queue) - arbitrary JSON
queue.process(async (job) => {
job.reportProgress({ percent: 50, message: 'halfway' });
return result;
});
// AFTER (glide-mq) - number (0-100) or object
const worker = new Worker('tasks', async (job) => {
await job.updateProgress(50);
await job.updateProgress({ page: 3, total: 10 }); // objects also supported
await job.log('halfway done'); // structured info goes to job.log()
return result;
}, { connection });
7. Stall Detection
// BEFORE (Bee-Queue) - manual setup required
const queue = new Queue('tasks', { stallInterval: 5000 });
queue.checkStalledJobs(5000); // must call manually!
// AFTER (glide-mq) - automatic on Worker
const worker = new Worker('tasks', processor, {
connection,
lockDuration: 30000,
stalledInterval: 30000,
maxStalledCount: 2
});
// Stall detection runs automatically - no manual call
8. Health Check
// BEFORE (Bee-Queue)
const health = await queue.checkHealth();
// { waiting, active, succeeded, failed, delayed, newestJob }
// AFTER (glide-mq)
const counts = await queue.getJobCounts();
// { waiting, active, completed, failed, delayed }
9. Web UI (Arena to Dashboard)
// BEFORE (Bee-Queue) - Arena
const Arena = require('bull-arena');
app.use('/', Arena({ Bee: require('bee-queue'), queues: [{ name: 'tasks' }] }));
// AFTER (glide-mq) - Dashboard
import { createDashboard } from '@glidemq/dashboard';
app.use('/dashboard', createDashboard([queue]));
What You Gain
Features Bee-Queue does not have that are available after migration:
| Feature | glide-mq API |
|---|---|
| Priority queues | { priority: 0 } (lower = higher, 0 is highest) |
| FlowProducer | Parent-child job trees and DAG workflows |
| Broadcast | Fan-out with subscriber groups |
| Batch processing | Process multiple jobs per worker call |
| Deduplication | Simple, throttle, and debounce modes |
| Schedulers | Cron patterns and interval repeatable jobs |
| Rate limiting | limiter: { max: 100, duration: 60000 } on Worker |
| LIFO mode | Process newest jobs first with { lifo: true } |
| Dead letter queue | deadLetterQueue: { name: 'dlq' } on Queue |
| Serverless pool | Connection caching for Lambda/Edge |
| HTTP proxy | Cross-language queue access via REST |
| OpenTelemetry | Automatic span emission |
| Testing utilities | TestQueue/TestWorker without Valkey |
| Cluster support | Hash-tagged keys, AZ-affinity routing |
| TLS / IAM auth | useTLS: true, IAM credentials for ElastiCache |
| Native TypeScript | Full generic type support throughout |
| AI usage tracking | job.reportUsage({ model, tokens, costs, ... }) |
| Token streaming | job.stream() / queue.readStream() for real-time LLM output |
| Suspend/resume | job.suspend() / queue.signal() for human-in-the-loop |
| Flow budget | flow.add(tree, { budget: { maxTotalTokens } }) |
| Fallback chains | opts.fallbacks: [{ model, provider }] |
| Dual-axis rate limiting | tokenLimiter for RPM + TPM compliance |
| Vector search | queue.createJobIndex() / queue.vectorSearch() |
Migration Checklist
- [ ] Install glide-mq, uninstall bee-queue and @types/bee-queue
- [ ] Create connection config (addresses array format)
- [ ] Convert queue.createJob().save() to queue.add(name, data, opts)
- [ ] Add job names to every queue.add() call (Bee-Queue had none)
- [ ] Convert .retries(n) to { attempts: n } (different name!)
- [ ] Convert .backoff(strategy, delay) to { backoff: { type, delay } }
- [ ] Convert .delayUntil(date) to { delay: ms }
- [ ] Convert .setId(id) to { jobId: id }
- [ ] Convert queue.process() to new Worker()
- [ ] Convert queue.saveAll() to queue.addBulk()
- [ ] Separate producer queues (isWorker:false to Producer class)
- [ ] Convert job.reportProgress(json) to job.updateProgress(number | object)
- [ ] Remove manual checkStalledJobs() calls (automatic on Worker)
- [ ] Convert checkHealth() to getJobCounts()
- [ ] Update event listeners (queue.on to worker.on or QueueEvents)
- [ ] Convert per-job events (job.on) to QueueEvents
- [ ] Keep the project's existing module system (CommonJS or ESM)
- [ ] Run full test suite
- [ ] Confirm queue counts: await queue.getJobCounts()
- [ ] Confirm no jobs stuck in active state
- [ ] Smoke-test QueueEvents or SSE listeners if the app exposes them
- [ ] Confirm workers, queues, and connections close cleanly
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
queue.createJob is not a function |
API changed | Use queue.add(name, data, opts) |
queue.process is not a function |
Separated producer/consumer | Use new Worker(name, handler, opts) |
Cannot use require() |
Module system mismatch | Keep the project's existing module system; glide-mq supports CommonJS and ESM |
job.reportProgress is not a function |
API renamed | Use job.updateProgress(number) |
Cannot find module 'bee-queue' |
Leftover import | grep -r "bee-queue" src/ to find remaining |
Missing job name |
Bee-Queue had no name | Add a name as first arg to queue.add() |
retries option not recognized |
Different name | Use attempts not retries |
| No stall detection | Bee-Queue needed manual start | glide-mq runs it automatically on Worker |
| Progress type changed | Bee-Queue accepted any JSON | Use job.updateProgress(number | object) - numbers (0-100) or objects supported |
| Per-job events not working | No per-job events in glide-mq | Use QueueEvents class and filter by jobId |
Quick Start Commands
npm uninstall bee-queue @types/bee-queue
npm install glide-mq
References
| Document | Content |
|---|---|
| references/api-mapping.md | Complete method-by-method API mapping |
| references/new-features.md | Features available after migration |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
glide-mq
Creates message queues, workers, job workflows, and fan-out broadcasts using glide-mq on Valkey/Redis Streams. Provides API reference, code patterns, and configuration for queues, workers, delayed/priority jobs, schedulers, batch processing, DAG workflows, request-reply, serverless producers, and AI-native primitives (usage tracking, token streaming, suspend/resume, budget caps, fallback chains, dual-axis rate limiting, rolling usage summaries, vector search, HTTP proxy/SSE). Triggers on "glide-mq", "glidemq", "job queue valkey", "background tasks valkey", "message queue redis streams", "glide-mq LLM queue", "glide-mq AI orchestration queue", "glide-mq token rate limiting", "glide-mq model fallback", "glide-mq human-in-the-loop queue", "glide-mq vector search", "glide-mq AI pipeline".
glide-mq-migrate-bullmq
Migrates Node.js applications from BullMQ to glide-mq. Covers connection config conversion, API mapping, breaking changes, and new features available after migration. Use when converting BullMQ queues and workers to glide-mq, replacing bullmq with glide-mq, or comparing BullMQ vs glide-mq APIs. Triggers on "bullmq to glide-mq", "replace bullmq with glide-mq", "migrate from bullmq", "switch from bullmq to glide-mq", "convert bullmq to glide-mq", "bullmq migration glide-mq".
ubiquitous-language
Extract a DDD-style ubiquitous language glossary from the current conversation, flagging ambiguities and proposing canonical terms. Saves to UBIQUITOUS_LANGUAGE.md. Use when user wants to define domain terms, build a glossary, harden terminology, create a ubiquitous language, or mentions "domain model" or "DDD".
every-style-editor
This skill should be used when reviewing or editing copy to ensure adherence to Every's style guide. It provides a systematic line-by-line review process for grammar, punctuation, mechanics, and style guide compliance.
manage-codex
Autonomous Codex batch orchestrator. Use for "/manage-codex", "manage codex", "use codex", "dispatch to codex", or long-running Codex work.
seo-audit
When the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," or "indexing issues." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo. For adding structured data, see schema-markup. For AI search optimization, see ai-seo.
Didn't find tool you were looking for?