In October 2024, we released a new SDK, @upstash/workflow, for Upstash Workflow, separating its development from the QStash SDK. Although Upstash Workflow is built on QStash, our goal is to improve the developer experience and support with a dedicated SDK. Development for Upstash Workflow will occur in @upstash/workflow, and Workflow-related imports will be removed from @upstash/qstash in future releases.

If you started using Upstash Workflow with @upstash/qstash, you will need to migrate to @upstash/workflow. We have made some backward-incompatible changes, but we aim to make the transition as smooth as possible. In this guide, we will explain the changes you may need to make for migration.

Install @upstash/workflow

First, we will need to install the new package with:

npm install @upstash/workflow

If you were using @upstash/qstash only for workflow, you can uninstall it from your project.

Serve methods

You will need to change the imports from @upstash/qstash to @upstash/workflow:

// old
import { serve } from "@upstash/qstash/nextjs"

// new 
import { serve } from "@upstash/workflow/nextjs"

We have updated what our serve methods return. We made this change to make it easier to extend the API in the future.

For instance, Next.js method changed like this:

// old
export const POST = serve(...);

// new
export const { POST } = serve(...);

We kept the serve method of Hono the same. The rest are updated in a similar way. See the quickstarts for the new way serve should be used.

Additionally, @upstash/workflow/nuxt import is removed. You should use @upstash/workflow/h3 instead. This change was made because nuxt uses h3 under the hood and our serve method for nuxt can work with any project using h3.

Updating context.call

If you were using context.call method in your workflow, you will need to change how it’s called and what it returns. Here is what the change looks like:

// old
const result = await context.call("call step", "<call-url>", "POST", ...)

// new
const {
  status,  // response status
  headers, // response headers
  body     // response body
} = await context.call("call step", {
  url: "<call-url>",
  method: "POST",
  ...
})

In the old version, we only returned the response body. Also, if the request to the url failed, the workflow run would fail.

In the new version, we update how the parameters are passed to the context.call. Additionally, we change the fail behavior: if the request fails, it doesn’t make the workflow fail. Instead, the status and the body is simply returned and workflow continues as usual.

If you have ongoing workflow runs which call context.call during your transition, status and headers fields may not be available in these old runs. After your transition, all workflow runs will have all three fields.