LOLapalooza
LOLapalooza

Reputation: 2082

Delay koa request 30 minutes but send 200 header immediately

I'm looking to delay all requests to an endpoint 30 minutes from when it was originally received. I'm working with Shopify and am responding to an order creation webhook, passing along data to another 3rd party service.

This is what happens: an order is placed, and then a webhook fires via Shopify, which contacts my koa endpoint with the associated order data.

I want to receive the webhook body (which includes order data) immediately, but then wait 30 minutes, then send along that information to another service. But I want to send a 200 header back to Shopify immediately, regardless of whether or not the code executes properly after the 30 minute delay (if it doesn't, I'll be informed via an error email anyway and can handle it manually, it wouldn't be the end of the world).

I've tried setTimeout methods, but the issue seems to be this: I can't send a 200 header back to Shopify until after the 30 minutes is up (or I shouldn't anyway, according to Koa docs?).

Meaning, if I do something like

context.response.status = 200; // tried to send response code immediately due to 30 minute timeout.
await handleRequest(context.request.body); // code waits 30 mins via settimeout

the 200 header isn't being sent right away

By the time 30 minutes has passed, Shopify has long given up on receiving a 200 header and has automatically put the request in a retry queue, which means the webhook will fire again before the 30 minutes is up, so I'll be sending duplicate requests to the service I'm trying to wait 30 minutes for.

I've considered cron, but it's not that I want these to be processed every 30 minutes, I want each individual request to wait 30 minutes to send to a third party service after it was received.

I'm really trying to avoid a database.

I'm fine with using setTimeout, if there is a way to send a 200 header back to Shopify before the timeout and subsequent service call after 30 minutes.

Upvotes: 0

Views: 549

Answers (1)

Chris Anderson
Chris Anderson

Reputation: 8515

Status code goes with the first part of the HTTP message, so you need to start sending, or end the HTTP response.

In this case, you don't need to block the response on the 3rd party call. Move the logic you want to run in 30 minutes into its own function and don't await on the timer. You don't need it in your response logic.

Some pseudo code:

function talkTo3rdPartyDelayed(context, delay) {
    setTimeout(()=>{
        // talk to 3rdparty
    }, delay);
    return; // continue what you were doing before, this is non-blocking
}

function listenForShopify(req, res, next) {
    talkTo3rdPartyDelayed({req.body}, 30*60*1000);
    res.statusCode = 200;
    next(); // or res.end() which sends the status code
}

As an aside, in a robust system, you'd store that 3rd party request you want delayed for 30 minutes in a queue system, with a visibility timer set to 30 minutes ahead. That way, if the process dies, you don't lose that work you intended to do. Having to manually handle any issues that come up will get tiresome.

Upvotes: 1

Related Questions