Need a message queue for serverless? Traditional queues like RabbitMQ need a persistent server. QStash works over HTTP.
What is QStash?
QStash is a serverless message queue and task scheduler from Upstash. Send a message via HTTP, QStash delivers it to your endpoint — with retries, delays, and scheduling.
Free Tier
- 500 messages/day
- 3 retries per message
- Cron schedules
- Delay and deduplication
Quick Start
bun add @upstash/qstash
import { Client } from '@upstash/qstash';
const qstash = new Client({ token: process.env.QSTASH_TOKEN! });
// Send a message to your API endpoint
await qstash.publishJSON({
url: 'https://your-app.com/api/process-order',
body: { orderId: '123', userId: 'user_456' },
});
Receiving Messages
// api/process-order.ts (your endpoint)
import { Receiver } from '@upstash/qstash';
const receiver = new Receiver({
currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY!,
nextSigningKey: process.env.QSTASH_NEXT_SIGNING_KEY!,
});
export async function POST(req: Request) {
// Verify the message came from QStash
const body = await req.text();
const isValid = await receiver.verify({
signature: req.headers.get('upstash-signature')!,
body,
});
if (!isValid) return new Response('Unauthorized', { status: 401 });
const data = JSON.parse(body);
await processOrder(data.orderId);
return new Response('OK');
}
Delayed Messages
// Send email 1 hour after signup
await qstash.publishJSON({
url: 'https://your-app.com/api/welcome-email',
body: { userId: 'user_123' },
delay: 3600, // 1 hour in seconds
});
Scheduled Messages (Cron)
// Run every day at 9 AM
await qstash.schedules.create({
destination: 'https://your-app.com/api/daily-report',
cron: '0 9 * * *',
body: JSON.stringify({ type: 'daily' }),
});
// List schedules
const schedules = await qstash.schedules.list();
// Delete schedule
await qstash.schedules.delete('schedule_id');
Fan-out (Send to Multiple Endpoints)
await qstash.batchJSON([
{
url: 'https://your-app.com/api/send-email',
body: { orderId: '123' },
},
{
url: 'https://your-app.com/api/update-inventory',
body: { orderId: '123' },
},
{
url: 'https://your-app.com/api/notify-warehouse',
body: { orderId: '123' },
},
]);
Workflow Chains
import { Workflow } from '@upstash/qstash';
const workflow = new Workflow({ client: qstash });
await workflow.run('order-flow', async (ctx) => {
// Step 1
const payment = await ctx.call('charge-payment', {
url: 'https://your-app.com/api/charge',
body: { amount: 99.99 },
});
// Step 2 (runs after step 1 succeeds)
await ctx.call('reserve-inventory', {
url: 'https://your-app.com/api/inventory',
body: { items: ['item-1'] },
});
// Step 3
await ctx.call('send-confirmation', {
url: 'https://your-app.com/api/email',
body: { template: 'order-confirmed' },
});
});
QStash vs Alternatives
| Feature | QStash | SQS | BullMQ | Inngest |
|---|---|---|---|---|
| Serverless | Yes | Yes | No (needs Redis) | Yes |
| HTTP-based | Yes | SDK | Redis protocol | Yes |
| Cron | Yes | EventBridge | bull-board | Yes |
| Retries | Built-in | Built-in | Built-in | Built-in |
| Free Tier | 500 msg/day | 1M msg/mo | N/A | 5K runs/mo |
| Edge | Yes | No | No | Yes |
Need async data processing? Check out my Apify actors — queue-based web scraping at scale. For custom solutions, email spinov001@gmail.com.
Top comments (0)