|
2 | 2 | title: Use Cases |
3 | 3 | --- |
4 | 4 |
|
5 | | -TODO: andreas: rework and reenable this page after we have 2 use cases ready |
6 | | -https://linear.app/upstash/issue/QSTH-84/use-cases-summaryhighlights-of-recipes |
| 5 | +QStash is an HTTP-based messaging and scheduling service. You hand it a request, |
| 6 | +and QStash delivers it to your endpoint later — with retries, delays, ordering, |
| 7 | +rate limits, and a dead letter queue when things go wrong. |
7 | 8 |
|
8 | | -This section is still a work in progress. |
| 9 | +That makes it a fit for any work that shouldn't happen inside the request that |
| 10 | +triggered it: tasks that take too long, tasks that must survive a failure, tasks |
| 11 | +that must run on a schedule, and tasks that must not overwhelm the service they |
| 12 | +call. |
9 | 13 |
|
10 | | -We will be adding detailed tutorials for each use case soon. |
| 14 | +Because everything is HTTP, there is no consumer to keep running. Your existing |
| 15 | +API endpoints *are* the consumers, wherever they are deployed — Vercel, AWS |
| 16 | +Lambda, Cloudflare Workers, Fly.io, or your own servers. |
11 | 17 |
|
12 | | -Tell us on [Discord](https://discord.gg/w9SenAtbme) or |
13 | | -[X](https://x.com/upstash) what you would like to see here. |
| 18 | +## Background jobs |
14 | 19 |
|
15 | | -### Triggering Nextjs Functions on a schedule |
| 20 | +Serverless platforms cap how long a function can run. Anything heavier than a |
| 21 | +few seconds — video processing, report generation, importing a CSV, calling a |
| 22 | +slow third-party API — risks a timeout, and the user is waiting for it. |
16 | 23 |
|
17 | | -Create a schedule in QStash that runs every hour and calls a Next.js serverless |
18 | | -function hosted on Vercel. |
| 24 | +With QStash, your handler publishes a message and returns immediately. QStash |
| 25 | +calls a second endpoint that does the real work, retrying if it fails. |
19 | 26 |
|
20 | | -### Reset Billing Cycle in your Database |
| 27 | +```typescript |
| 28 | +import { Client } from "@upstash/qstash"; |
21 | 29 |
|
22 | | -Once a month, reset database entries to start a new billing cycle. |
| 30 | +const client = new Client({ token: process.env.QSTASH_TOKEN! }); |
23 | 31 |
|
24 | | -### Fanning out alerts to Slack, email, Opsgenie, etc. |
| 32 | +await client.publishJSON({ |
| 33 | + url: "https://your-app.com/api/process-video", |
| 34 | + body: { videoId }, |
| 35 | + retries: 3, |
| 36 | +}); |
| 37 | +``` |
25 | 38 |
|
26 | | -Createa QStash URL Group that receives alerts from a single source and delivers them |
27 | | -to multiple destinations. |
| 39 | +If the job itself is longer than a single function invocation allows, use |
| 40 | +[callbacks](/qstash/features/callbacks) so QStash delivers the response to |
| 41 | +another endpoint once it's ready, instead of your caller blocking on it. |
28 | 42 |
|
29 | | -### Send delayed message when a new user signs up |
| 43 | +<Card |
| 44 | + title="Background Jobs" |
| 45 | + icon="share-all" |
| 46 | + href="/qstash/features/background-jobs" |
| 47 | +> |
| 48 | + Full walkthrough, including local development |
| 49 | +</Card> |
30 | 50 |
|
31 | | -Publish delayed messages whenever a new user signs up in your app. After a |
32 | | -certain delay (e.g. 10 minutes), QStash will send a request to your API, |
33 | | -allowing you to email the user a welcome message. |
| 51 | +## Scheduled and recurring tasks |
| 52 | + |
| 53 | +Anything you would put in a cron job — nightly reports, resetting billing |
| 54 | +cycles, expiring trials, syncing a search index, warming a cache — becomes a |
| 55 | +[schedule](/qstash/features/schedules) that calls your endpoint on a cron |
| 56 | +expression. |
| 57 | + |
| 58 | +```typescript |
| 59 | +await client.schedules.create({ |
| 60 | + destination: "https://your-app.com/api/daily-report", |
| 61 | + cron: "0 8 * * *", |
| 62 | +}); |
| 63 | +``` |
| 64 | + |
| 65 | +Schedules run in UTC by default and support |
| 66 | +[timezones](/qstash/features/schedules#timezones). Unlike platform-native cron |
| 67 | +(such as Vercel Cron), schedules are not tied to a deploy, are not limited to |
| 68 | +one per plan tier, and retry on failure. |
| 69 | + |
| 70 | +## Reliable webhook delivery |
| 71 | + |
| 72 | +Webhooks are the most common reason people reach for QStash, in both |
| 73 | +directions: |
| 74 | + |
| 75 | +**Receiving webhooks.** Point Stripe, GitHub, Shopify, or Clerk at a QStash |
| 76 | +publish URL instead of your endpoint directly. QStash absorbs the burst, retries |
| 77 | +if your app is down or mid-deploy, and applies whatever delay, timeout, or |
| 78 | +[flow control](/qstash/features/flowcontrol) you configure. The provider gets a |
| 79 | +fast 2xx even when your processing is slow. |
| 80 | + |
| 81 | +**Sending webhooks.** If you deliver webhooks to your own customers, QStash |
| 82 | +handles the part nobody wants to build: exponential retries, per-customer |
| 83 | +concurrency limits, and a [dead letter queue](/qstash/features/dlq) for |
| 84 | +endpoints that stay down. |
| 85 | + |
| 86 | +<CardGroup cols={2}> |
| 87 | + <Card title="Use as Webhook Receiver" icon="webhook" href="/qstash/howto/webhook"> |
| 88 | + Publish URLs, URL Groups, and header forwarding |
| 89 | + </Card> |
| 90 | + <Card |
| 91 | + title="Building Reliable & Type-Safe Webhooks" |
| 92 | + icon="book" |
| 93 | + href="https://upstash.com/blog/webhook-system-with-qstash" |
| 94 | + > |
| 95 | + Designing an outbound webhook system on QStash |
| 96 | + </Card> |
| 97 | +</CardGroup> |
| 98 | + |
| 99 | +## Fan-out to multiple services |
| 100 | + |
| 101 | +One event often needs to reach several places: a purchase should trigger a |
| 102 | +receipt email, a Slack notification, an analytics event, and a warehouse |
| 103 | +webhook. |
| 104 | + |
| 105 | +Publish once to a [URL Group](/qstash/features/url-groups) and QStash creates an |
| 106 | +independent, independently-retried delivery for each subscribed endpoint. Adding |
| 107 | +or removing a consumer is a URL Group change — no redeploy of the producer. |
| 108 | + |
| 109 | +```typescript |
| 110 | +await client.publishJSON({ |
| 111 | + urlGroup: "order-created", |
| 112 | + body: { orderId }, |
| 113 | +}); |
| 114 | +``` |
| 115 | + |
| 116 | +The same shape works for alerting: one alert source fanned out to Slack, email, |
| 117 | +and PagerDuty. |
| 118 | + |
| 119 | +## Rate-limited and fragile third-party APIs |
| 120 | + |
| 121 | +When you call an API with a quota — OpenAI, Resend, Shopify, a partner's |
| 122 | +internal service — the hard part is not calling it, it's not calling it too |
| 123 | +often. [Flow Control](/qstash/features/flowcontrol) lets QStash hold messages |
| 124 | +back for you, by request rate, by concurrency, or both. |
| 125 | + |
| 126 | +```typescript |
| 127 | +await client.publishJSON({ |
| 128 | + url: "https://your-app.com/api/summarize", |
| 129 | + body: { articleId }, |
| 130 | + flowControl: { key: "openai", parallelism: 5, rate: 60, period: "1m" }, |
| 131 | +}); |
| 132 | +``` |
| 133 | + |
| 134 | +You can publish ten thousand messages at once and let QStash drip them out at |
| 135 | +the rate your downstream tolerates, instead of building a queue and a limiter |
| 136 | +yourself. Limits apply per key, so the same key can span multiple URLs. |
| 137 | + |
| 138 | +<Card |
| 139 | + title="Efficient Article Summarization with QStash" |
| 140 | + icon="book" |
| 141 | + href="https://upstash.com/blog/article-summarizer-qstash-python" |
| 142 | +> |
| 143 | + Handling API rate limits and parallel processing in Python |
| 144 | +</Card> |
| 145 | + |
| 146 | +## AI and LLM requests |
| 147 | + |
| 148 | +LLM calls are slow, variable, and expensive to retry by hand — a bad match for a |
| 149 | +10-second serverless timeout. QStash gives them a 2-hour HTTP timeout, delivers |
| 150 | +the response to a [callback](/qstash/features/callbacks) endpoint when it's |
| 151 | +done, and can [batch](/qstash/features/batch) many requests in one publish. |
| 152 | + |
| 153 | +There are built-in integrations for [OpenAI-compatible |
| 154 | +providers](/qstash/integrations/llm) and [Anthropic](/qstash/integrations/anthropic), |
| 155 | +so QStash calls the provider for you and you only handle the callback. |
| 156 | + |
| 157 | +Combined with flow control, this is a practical way to run bulk embedding jobs, |
| 158 | +document summarization, or content generation without hitting provider rate |
| 159 | +limits. |
| 160 | + |
| 161 | +## Delayed and time-based messages |
| 162 | + |
| 163 | +Some work is defined by *when* it should happen: a welcome email 10 minutes |
| 164 | +after signup, a trial-ending reminder 3 days out, an abandoned-cart nudge, a |
| 165 | +retry of a payment tomorrow. |
| 166 | + |
| 167 | +[Delay](/qstash/features/delay) a message by a duration or to an absolute |
| 168 | +timestamp, and QStash holds it until then — up to 7 days on the free plan and up |
| 169 | +to a year on pay-as-you-go. |
| 170 | + |
| 171 | +```typescript |
| 172 | +await client.publishJSON({ |
| 173 | + url: "https://your-app.com/api/send-welcome-email", |
| 174 | + body: { userId }, |
| 175 | + delay: "10m", |
| 176 | +}); |
| 177 | +``` |
| 178 | + |
| 179 | +With the [Resend integration](/qstash/integrations/resend) you can skip the |
| 180 | +endpoint entirely and have QStash send the email itself at the scheduled time. |
| 181 | + |
| 182 | +<CardGroup cols={2}> |
| 183 | + <Card |
| 184 | + title="Scheduling emails in the user's timezone" |
| 185 | + icon="book" |
| 186 | + href="https://upstash.com/blog/timezone-scheduling-emails" |
| 187 | + > |
| 188 | + Per-user send times with QStash |
| 189 | + </Card> |
| 190 | + <Card |
| 191 | + title="Building an Email Scheduler" |
| 192 | + icon="book" |
| 193 | + href="https://upstash.com/blog/email-scheduler-qstash-python" |
| 194 | + > |
| 195 | + An email scheduler with the Python SDK |
| 196 | + </Card> |
| 197 | +</CardGroup> |
| 198 | + |
| 199 | +## Ordered processing |
| 200 | + |
| 201 | +Some pipelines break if messages overtake each other — applying a sequence of |
| 202 | +updates to the same record, processing a customer's events in order, or writing |
| 203 | +to a system that can't handle concurrent writes. |
| 204 | + |
| 205 | +[Queues](/qstash/features/queues) deliver messages one at a time in FIFO order. |
| 206 | +The next message only becomes active after the current one is delivered, has |
| 207 | +exhausted its retries, or its callback has finished. |
| 208 | + |
| 209 | +```typescript |
| 210 | +const queue = client.queue({ queueName: "user-123-events" }); |
| 211 | + |
| 212 | +await queue.enqueueJSON({ |
| 213 | + url: "https://your-app.com/api/apply-event", |
| 214 | + body: { event }, |
| 215 | +}); |
| 216 | +``` |
| 217 | + |
| 218 | +## Syncing and periodic data updates |
| 219 | + |
| 220 | +Instead of querying a slow or rate-limited third-party API on every request, |
| 221 | +schedule a job that pulls fresh data into your own database, and serve reads |
| 222 | +from there. The same pattern covers flushing Redis state to a primary database, |
| 223 | +refreshing a cache, and rebuilding a search index. |
| 224 | + |
| 225 | +<CardGroup cols={2}> |
| 226 | + <Card |
| 227 | + title="Periodic Data Updates" |
| 228 | + icon="rotate" |
| 229 | + href="/qstash/recipes/periodic-data-updates" |
| 230 | + > |
| 231 | + Recipe: keep third-party data fresh in your own database |
| 232 | + </Card> |
| 233 | + <Card |
| 234 | + title="Sync Redis state to your database" |
| 235 | + icon="book" |
| 236 | + href="https://upstash.com/blog/syncing-state-with-qstash" |
| 237 | + > |
| 238 | + Write-behind from Redis using QStash |
| 239 | + </Card> |
| 240 | +</CardGroup> |
| 241 | + |
| 242 | +## Decoupling services |
| 243 | + |
| 244 | +Beyond individual jobs, QStash works as the messaging layer between your |
| 245 | +services: producers publish, QStash guarantees |
| 246 | +[at-least-once delivery](/qstash/features/at-least-once), and consumers are just |
| 247 | +HTTP endpoints. [Deduplication](/qstash/features/deduplication) keeps retries |
| 248 | +from double-processing, [signature verification](/qstash/features/security) |
| 249 | +proves a request came from QStash, and the DLQ holds anything that never |
| 250 | +succeeded. |
| 251 | + |
| 252 | +This is the pattern behind cutting serverless costs, too: move expensive work |
| 253 | +out of long-running function invocations and let QStash drive short, cheap ones. |
| 254 | + |
| 255 | +<Card |
| 256 | + title="Get Rid of Function Timeouts and Reduce Vercel Costs" |
| 257 | + icon="book" |
| 258 | + href="https://upstash.com/blog/vercel-cost-workflow" |
| 259 | +> |
| 260 | + Why offloading work changes your bill |
| 261 | +</Card> |
| 262 | + |
| 263 | +## Multi-step workflows |
| 264 | + |
| 265 | +If your task has several dependent steps — call an API, wait for a human, |
| 266 | +branch, then call another — chaining QStash messages by hand gets awkward. |
| 267 | +[Upstash Workflow](/workflow/getstarted) is built on QStash and gives you |
| 268 | +durable, resumable functions where each step is checkpointed automatically. |
| 269 | + |
| 270 | +<Tip href="/workflow/getstarted"> |
| 271 | +Use QStash directly for single messages, schedules, and fan-out. Reach for |
| 272 | +[Upstash Workflow](/workflow/getstarted) when the logic spans multiple dependent |
| 273 | +steps. |
| 274 | +</Tip> |
| 275 | + |
| 276 | +## More examples |
| 277 | + |
| 278 | +<CardGroup cols={2}> |
| 279 | + <Card |
| 280 | + title="Building a seriously reliable serverless API" |
| 281 | + icon="book" |
| 282 | + href="https://upstash.com/blog/build-reliable-serverless-api" |
| 283 | + > |
| 284 | + Retries, idempotency, and failure handling end to end |
| 285 | + </Card> |
| 286 | + <Card |
| 287 | + title="Decouple Webhook Processing on Next.js" |
| 288 | + icon="book" |
| 289 | + href="https://upstash.com/blog/webhook-qstash" |
| 290 | + > |
| 291 | + Taking webhook work off the request path |
| 292 | + </Card> |
| 293 | + <Card |
| 294 | + title="Build a Subscription Service with Next.js & Prisma" |
| 295 | + icon="book" |
| 296 | + href="https://upstash.com/blog/saas-subscription" |
| 297 | + > |
| 298 | + Recurring billing cycles driven by schedules |
| 299 | + </Card> |
| 300 | + <Card |
| 301 | + title="Refresh stale data in a SvelteKit app" |
| 302 | + icon="book" |
| 303 | + href="https://upstash.com/blog/sveltekit-qstash" |
| 304 | + > |
| 305 | + Scheduled revalidation outside the request path |
| 306 | + </Card> |
| 307 | + <Card |
| 308 | + title="Serverless Background Jobs and Message Queues Compared" |
| 309 | + icon="scale-balanced" |
| 310 | + href="https://upstash.com/blog/serverless-background-jobs-and-message-queues-every-major-option-in-2026" |
| 311 | + > |
| 312 | + How QStash compares to the alternatives |
| 313 | + </Card> |
| 314 | + <Card |
| 315 | + title="Why We Chose QStash at Scale" |
| 316 | + icon="book" |
| 317 | + href="https://upstash.com/blog/qstash-workflow-at-scale" |
| 318 | + > |
| 319 | + A production user's account of running QStash |
| 320 | + </Card> |
| 321 | +</CardGroup> |
| 322 | + |
| 323 | +More posts are on the [QStash blog](https://upstash.com/blog/tag/qstash). If |
| 324 | +there's a use case you'd like documented, tell us on |
| 325 | +[Discord](https://upstash.com/discord) or [X](https://x.com/upstash). |
0 commit comments