req.signal.addEventListener doesn't work in Vercel function

I need to call cleanup code if the function is aborted. What I had working locally with next dev was using an event listener, but this is not called when deplyed on Vercel.

export async function GET(req: NextRequest, res: NextResponse) {
...
    req.signal.addEventListener('abort', () => {
        console.log('Request aborted')
        cleanup()
    }
...
}

Is there a proper way to detect when a request has been cancelled?

Hi, @mozeryansky! Welcome to the Vercel Community :smile:

The approach you are using with addEventListener on req.signal is actually on the right track, but there’s a slight modification we can make to ensure it works both locally and when deployed on Vercel.

Instead of using addEventListener, we can use the AbortSignal.onabort property, which is supported in both Node.js and Edge runtimes on Vercel.

Here's an example:
export async function GET(req: NextRequest) {
  let aborted = false

  req.signal.onabort = () => {
    console.log('Request aborted')
    aborted = true
    cleanup()
  }

  try {
    // Your main logic here
    const result = await someAsyncOperation(req.signal)

    if (aborted) {
      throw new Error('Request aborted')
    }

    return NextResponse.json({ result })
  } catch (error) {
    if (error.name === 'AbortError' || aborted) {
      return NextResponse.json({ message: 'Request was aborted' }, { status: 499 })
    }
    // Handle other errors
    return NextResponse.json({ error: error.message }, { status: 500 })
  }
}

function cleanup() {
  // Your cleanup logic here
  console.log('Performing cleanup operations')
}

async function someAsyncOperation(signal: AbortSignal) {
  // Simulate a long-running operation that can be aborted
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => resolve('Operation completed'), 5000)
    signal.addEventListener('abort', () => {
      clearTimeout(timeout)
      reject(new Error('Operation aborted'))
    })
  })
}
  1. We use req.signal.onabort to set up the abort handler. This will be called if the request is cancelled.
  2. We maintain an aborted flag to track the cancellation state throughout the request lifecycle.
  3. In the someAsyncOperation function, we demonstrate how to make a cancellable operation using the AbortSignal. This is crucial for long-running operations that need to be cancellable.
  4. In the error handling, we check for both AbortError and our aborted flag. This covers cases where the abort might be triggered by the operation itself or by our manual flag.
  5. We use status code 499 for aborted requests, which is a common convention for client-closed requests.

Let us know how you get on!

Hi, that solution does not work for me. I copied your code into a new route, deployed using vercel --prod, sent a request using curl and immediately cancelled. I see the request logs show it ran for 5s and returned a 200 without logging anything, so it appears the function completed successfully and was not aborted.

Does it work when you deploy it? Is there any settings I need to change?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.