Skip to content

@glidemq/hono

REST API and real-time SSE for glide-mq job queues, as Hono middleware. One middleware + one router -- declare queues, get 21 endpoints with type-safe RPC.

Why @glidemq/hono

  • Type-safe RPC client -- export GlideMQApiType and use Hono's hc<> for end-to-end typed HTTP calls with zero codegen
  • Edge and serverless ready -- lightweight Producer re-exports let you enqueue jobs from Cloudflare Workers, Vercel Edge Functions, or Deno Deploy without pulling in full Queue/Worker machinery. Workers and SSE require a long-lived runtime (Node, Bun, Deno).
  • Multi-runtime -- Hono runs on Node, Deno, Bun, and edge runtimes; this middleware follows
  • Two imports, full API -- glideMQ() middleware + glideMQApi() router gives you 21 endpoints, SSE events, and scheduler CRUD
  • Optional Zod validation -- install zod + @hono/zod-validator for request validation; works fine without them

Install

bash
npm install @glidemq/hono glide-mq hono

Optional Zod validation:

bash
npm install zod @hono/zod-validator

Requires glide-mq 0.9+.

Quick Start

ts
import { Hono } from 'hono';
import { glideMQ, glideMQApi } from '@glidemq/hono';

const app = new Hono();

app.use(
  glideMQ({
    connection: { addresses: [{ host: 'localhost', port: 6379 }] },
    queues: {
      emails: {
        processor: async (job) => {
          await sendEmail(job.data.to, job.data.subject);
          return { sent: true };
        },
        concurrency: 5,
      },
      reports: {},
    },
  }),
);

app.route('/api/queues', glideMQApi());

export default app;

How It Works

glideMQ(config) is a Hono middleware that creates a QueueRegistry and injects it into every request as c.var.glideMQ. Queues and workers are initialized lazily on first access; producers are created eagerly when requested.

glideMQApi(opts?) returns a typed Hono sub-router with all 21 REST endpoints. Mount it at any path with app.route(). It reads the registry from c.var.glideMQ -- the middleware must be applied first.

You can also pass a pre-built QueueRegistryImpl for graceful shutdown control:

ts
const registry = new QueueRegistryImpl({ connection, queues: { emails: { processor } } });
app.use(glideMQ(registry));
process.on('SIGTERM', () => registry.closeAll());

Type-Safe RPC Client

Hono's hc client infers route types from the router, giving you end-to-end typed HTTP calls with no codegen and no OpenAPI spec:

ts
import { hc } from 'hono/client';
import type { GlideMQApiType } from '@glidemq/hono';

const client = hc<GlideMQApiType>('http://localhost:3000/api/queues');

const res = await client[':name'].jobs.$post({
  param: { name: 'emails' },
  json: { name: 'welcome', data: { to: 'user@example.com' } },
});
const job = await res.json(); // typed as JobResponse

Endpoints

Jobs

MethodRouteDescription
POST/:name/jobsAdd a job
POST/:name/jobs/waitAdd a job and wait for result
GET/:name/jobsList jobs (query: type, start, end, excludeData)
GET/:name/jobs/:idGet a single job
POST/:name/jobs/:id/priorityChange job priority
POST/:name/jobs/:id/delayChange job delay
POST/:name/jobs/:id/promotePromote a delayed job

Queue Operations

MethodRouteDescription
GET/:name/countsGet job counts by state
GET/:name/metricsGet queue metrics (query: type, start, end)
POST/:name/pausePause queue
POST/:name/resumeResume queue
POST/:name/drainDrain waiting jobs
POST/:name/retryRetry failed jobs
DELETE/:name/cleanClean old jobs (query: grace, limit, type)
GET/:name/workersList active workers
GET/:name/eventsSSE event stream
POST/:name/produceAdd a job via Producer (lightweight, serverless)

Schedulers

MethodRouteDescription
GET/:name/schedulersList all schedulers
GET/:name/schedulers/:schedulerNameGet a single scheduler
PUT/:name/schedulers/:schedulerNameUpsert a scheduler
DELETE/:name/schedulers/:schedulerNameRemove a scheduler

Features

  • 21 REST endpoints -- jobs, counts, metrics, pause/resume, drain, retry, clean, workers, SSE events, schedulers, and producers
  • Type-safe RPC -- hc<GlideMQApiType> gives end-to-end typed HTTP calls with no codegen
  • Edge/serverless producers -- re-exports Producer, ServerlessPool, and serverlessPool from glide-mq for lightweight job enqueuing without worker overhead
  • Real-time SSE -- streams completed, failed, progress, active, waiting, stalled, and heartbeat events via Hono's streamSSE
  • Queue access control -- restrict which queues and producers are exposed via GlideMQApiConfig
  • Optional Zod validation -- auto-detected at startup; degrades gracefully to manual parsing when not installed
  • Scheduler CRUD -- create, read, update, and delete repeatable job schedulers (cron or interval)
  • Testing mode -- createTestApp() uses in-memory TestQueue/TestWorker, no Valkey required

Configuration

GlideMQConfig

ts
interface GlideMQConfig {
  connection?: ConnectionOptions; // Required unless testing: true
  queues?: Record<string, QueueConfig>;
  producers?: Record<string, ProducerConfig>;
  prefix?: string;      // Key prefix (default: 'glide')
  testing?: boolean;    // In-memory mode, no Valkey needed
  serializer?: Serializer;
}

interface QueueConfig {
  processor?: (job: Job) => Promise<any>; // Omit for producer-only queues
  concurrency?: number;                   // Default: 1
  workerOpts?: Record<string, unknown>;
}

interface ProducerConfig {
  compression?: 'none' | 'gzip';
  serializer?: Serializer;
}

GlideMQApiConfig

ts
interface GlideMQApiConfig {
  queues?: string[];     // Restrict to specific queue names
  producers?: string[];  // Restrict to specific producer names
}

Producers are lightweight alternatives to queues for serverless/edge -- they only support add() and addBulk(), return string IDs, and carry no worker or event-emitter overhead. Configure them alongside queues and use POST /:name/produce or access them directly via c.var.glideMQ.getProducer(name).

Testing

No Valkey needed. createTestApp wires middleware and router in testing mode using in-memory TestQueue/TestWorker:

ts
import { createTestApp } from '@glidemq/hono/testing';

const { app, registry } = createTestApp({
  emails: { processor: async (job) => ({ sent: true }) },
});

const res = await app.request('/emails/jobs', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'welcome', data: { to: 'user@example.com' } }),
});

expect(res.status).toBe(201);
await registry.closeAll();

Note: SSE in testing mode emits counts events (polling-based state diffs) rather than job lifecycle events.

Direct Registry Access

Access the registry in your own routes via c.var.glideMQ:

ts
app.post('/send-email', async (c) => {
  const { queue } = c.var.glideMQ.get('emails');
  const job = await queue.add('send', { to: 'user@example.com', subject: 'Hello' });
  return c.json({ jobId: job?.id });
});

Limitations

  • Graceful shutdown is manual -- call registry.closeAll() yourself (Hono has no lifecycle hooks like Fastify's onClose)
  • SSE requires a long-lived connection; edge runtimes with short execution limits may not support it
  • Producers are not available in testing mode; use queues instead
  • Queue names must match /^[a-zA-Z0-9_-]{1,128}$/

Ecosystem

PackageDescription
glide-mqCore queue library -- producers, workers, schedulers, workflows
@glidemq/honoHono REST API + SSE middleware (you are here)
@glidemq/fastifyFastify REST API + SSE plugin
@glidemq/hapiHapi REST API + SSE plugin
@glidemq/nestjsNestJS module -- decorators, DI, lifecycle management
@glidemq/dashboardExpress web UI for monitoring and managing queues

License

Apache-2.0

Released under the Apache-2.0 License.