Why does my express error handler not work with "fetch" or "xhr" requests?

88 views Asked by At

I have an error-catching wrapper function

and a error-handling wrapper function for my route handlers:

function catchAsync(func) {
return function (res, req, next) {
func(req, res, next).catch((err) => {
next(err)
})
}}

which wraps around my express route-handling-functions (async(req,res)=>{...logic...})like so:

app.put('/note/:noteId', catchAsync(async(req,res)=>{
...logic...
}))

And a custom error handler at the end of my app.js:

app.use((err, req, res, next) => {
const { status = 500 } = err
res.status(status).render('error', { error: err, status })
})

..which renders the error.ejs with the error message and status

When I intentionally trigger an error through leaving a required form field blank (after DOMPurify.sanitize-ing it), express catches it fine when I submit a form through the express-handling regular/default way,but when I use a custom submitter (so the page won't reload automatically) with "fetch" (which works fine for regular use),

custom form-submitter-function:

const saveEdit = (address,...etc) => {
    fetch(`${address}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `${...}`,
    })
        .then(resp => resp.json())//this part throws a second error cuz it get's nothing back
        .then(...)
        .catch((err)=>{console.log('error in fetch', err)})
    }

the expresses error handler doesn't handle the error passed off from catchAsync and render the error.ejs page. Instead my fetch request gets nothing back and and so the resp.json() throws its own error (unexpected token...blahblah) which i get after the "Internal Server Error" (which express was supposed throw an error page for).

Can anyone explain why this happens and if there's a way that I can make it so the custom express error handler will throws up the page, even if the request is made with "fetch/xhr"? Thanks!

I've tried a bunch of things including modifying catchAsync so it manually renders the error page, but I guess "res.render" doesn't work unless it's after an app.verb() ------ Here's the full code of the functions, if it's of any help:

const saveEdit = (edittedNoteForm, edittedNote, extras, address, edittedContent) => {
    //format data to urlencoded
    const editNoteFD = new FormData(edittedNoteForm)
    const editNoteUrlEnc = formDataToURLEncoded(editNoteFD, edittedContent)

    //send to route and have route send back note object
    fetch(`${address}`,{// in this case, address="/note/:noteId"
        method: 'PUT',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: `${editNoteUrlEnc}&extras=${extras}`,
    })
        .then(resp => resp.json())
        .then(responseNote => {
            //get html of a updated note for display
            edittedNote.innerHTML = formatNoteHTML(responseNote)
            //add event listeners to "edit" and "save" buttons/forms
            addSubmitListenerToChildElement(edittedNote, 'edit_note', getEditForm) //1st param parent elment, second child's ID, third, callback
            addSubmitListenerToChildElement(edittedNote, 'delete_note', deleteNote)
        })
        .catch((err)=>{console.log('error in fetch', err)})
    }
app.put('/note/:noteId', catchAsync(async(req,res)=>{
    const edittedNoteReqBody = parseAndCleanReqBody(req.body)
    let note = await Note.findById(req.params.noteId)
    if (edittedNoteReqBody.extras === 'false'){ // only the note.text field has been changed
        note.note.text = edittedNoteReqBody.note.text
    } else { // rewrite all form inputs
        const {note: noteNested, section, video} = edittedNoteReqBody
        note.note = noteNested
        note.section = section;
        note.video = video;
    }
    await note.save()
    res.send(note)
}))
0

There are 0 answers