Consider this basic worker that needs to respond to the caller immedialty, then hand off the request to another API.
async function saveToDatabase(notification) {
return fetch('https://example.com/api', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(notification, null, 0)
});
}
async function saveRequest(request) {
let body;
const contentType = request.headers.get('content-type')
if (contentType.includes('application/json')) {
body = await request.json();
}
if (body) {
return saveToDatabase(body);
}
}
async function handleRequest(event) {
if (event.request.method === 'POST') {
event.waitUntil(saveRequest(event.request));
// try {
// await saveRequest(event.request);
// } catch (error) {
// return new Response(error.toString(), { status: 500 })
// }
return new Response(undefined, { status: 200 })
}
return new Response(undefined, { status: 405 })
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event));
})
The function in waitUntil is never executed. If however, I await the function as shown in the commented out code - it works. So the handling + posting obviously works, but I don’t want to force the caller to wait.
One of the quirks of the Service Worker specification is that It’s not possible to read event.request's body after having generated a response, so I expect that the call to request.json() is resulting in an exception.
A quick workaround would be to read the request body up front, then call event.waitUntil() and return the Response object.
Edit: I had thought this behavior was mandated by the Service Worker spec, but after searching for a reference, I’m not so sure. At any rate, what I described is accurate for our implementation – still researching why.
That solved it for me thank you @harris. I await loading the body before beginning processing the response or the waitUntil. I tested this with an setTimeout to exaggerate the delay and the request responded instantly, while the saveToDatabase function would now be called after.
async function handleRequest(event) {
if (event.request.method === 'POST') {
const body = await getBody(event.request);
if (body) event.waitUntil(saveToDatabase(body)); // this might actually take a few seconds
return new Response(undefined, { status: 200 }) // caller sees instant response regardless
}
return new Response(undefined, { status: 405 })
}