Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix 'TypeError: Failed to fetch'?

I'm getting a TypeError: Failed to fetch error when I attempt to send a post request using fetch on the front-end and an express route on the back-end.

I'm able to successfully create the new user in the db, but when attempting to obtain that new user data through the fetch promise, that's when the error is being thrown.

app.js

function createNewUser() {
  let formUsername = document.getElementById('signup-username').value;
  let formEmail = document.getElementById('signup-email').value;
  let formPassword = document.getElementById('signup-password').value;
  let url = "/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }

  fetch(url, {
    method: 'POST',
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
        'Content-Type': 'application/json'
    },
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer',
    body: JSON.stringify(newUserData),
  }).then(res => res.json())
  .then(response => console.log('Success: ', JSON.stringify(response)))
  .catch(error => console.error('Error: ', error));
}

users.js

router.post('/users', function(req, res) {
   User.create(req.body)
   .then(function(user) {
      res.json({
         user: user
      })
   }
});

server.js

const express = require('express');
const app = express();
const fs = require('fs');
const path = require('path');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const auth = require('./auth');
const router = require('./routes/routes.js');

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(router);

app.use('/', express.static(path.join(__dirname, 'public')));

app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader(
    "Access-Control-Allow-Methods",
    "OPTIONS, GET, POST, PUT, PATCH, DELETE" // what matters here is that OPTIONS is present
  );
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization", "Access-Control-Allow-Origin");
  next();
});

app.listen(3000, function(){
  console.log("Listening on port 3000");
});

I need to get that user object back in order to access its data.

Edit: So, I've figured out that the issue has to do with how the request is submitted on the front-end. If I create the following function and then call it when app.js is loaded, then everything works:

function createNewUserTest() {
  let formUsername = 'dd';
  let formEmail = '[email protected]';
  let formPassword = 'secrete';
  let url = "/api/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }
  fetch(url, {
    method: 'POST',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(newUserData),
  })
  .then(res => res.json())
  .then(response => console.log('Success: ', response))
  .catch(error => console.error('Error: ', error));
}

createNewUserTest();

But, if I try to call this function either through onsubmit in the form or onclick on the button in the html, or if I use an event listener (see below, which is in app.js), then I get the TypeError: Failed to fetch error:

let signupSubmitButton = document.getElementById('signup-submit');
signupSubmitButton.addEventListener('click', createNewUserTest);

This is even more baffling to me. I'm required to use Vanilla JS and I need to create the user through a form submission, but not sure what I need to adjust here.

Solution Foiled by the event.preventDefault() again. This was all I needed.

let signupForm = document.getElementById('signup-form');
signupForm.addEventListener('submit', function(event) {
  event.preventDefault();
  let formUsername = document.getElementById('signup-username').value;
  let formEmail = document.getElementById('signup-email').value;
  let formPassword = document.getElementById('signup-password').value;
  let url = "/api/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }
  fetch(url, {
    method: 'POST',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(newUserData),
  })
  .then(res => res.json())
  .then(response => console.log('Success: ', response))
  .catch(error => console.error('Error: ', error));
});
like image 373
Micah Wierenga Avatar asked Jul 04 '19 17:07

Micah Wierenga


2 Answers

The issue was that the page was reloading, which kept me from getting the data back in time. The solution was to simply add event.preventDefault() inside the listener.

app.js

let signupForm = document.getElementById('signup-form');
signupForm.addEventListener('submit', function(event) {
  event.preventDefault();
  let formUsername = document.getElementById('signup-username').value;
  let formEmail = document.getElementById('signup-email').value;
  let formPassword = document.getElementById('signup-password').value;
  let url = "/api/users";
  let newUserData = {
    username: formUsername,
    email: formEmail,
    password: formPassword
  }
  fetch(url, {
    method: 'POST',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(newUserData),
  })
  .then(res => res.json())
  .then(response => console.log('Success: ', response))
  .catch(error => console.error('Error: ', error));
});
like image 177
Micah Wierenga Avatar answered Nov 12 '22 00:11

Micah Wierenga


The question is about "TypeError failed to fetch". The wording of the message sends one in the direction of network/server/CORS type issues as explored in other answers, but there is one cause I have discovered that is completely different.

I had this problem and took it at face value for some time, especially puzzled because it was provoked by my page POSTing in Chrome but not in Firefox.

It was only after I discovered chrome://net-internals/#events and saw that my request suffered from 'delegate_blocked_by = "Opening Files"' that I finally had a clue.

My request was POSTing a file uploaded from the user's computer via a file input element. This file happened to be a file open in Excel. Although it POSTed fine from Firefox, it was only when closed that it could be posted in Chrome.

Users of your web application need to be advised about this potential issue, and web developers should also be aware that "TypeError failed to fetch" can sometimes mean "TypeError didn't get as far as trying to fetch"

like image 6
Iain MacKay Avatar answered Nov 12 '22 01:11

Iain MacKay