What's happening is that when your server returns a status code (like 400), it ends up as an exception in the client code (where you're executing your request). There's a couple of different ways to handle this, but the easiest way to is to do a try/catch
Here's an example of what they suggest on their GitHub:
/*
 * Handling Errors using async/await
 * Has to be used inside an async function
 */
try {
    const response = await axios.get('https://your.site/api/v1/bla/ble/bli');
    // Success 
    console.log(response);
} catch (error) {
    // Error 
    if (error.response) {
        /*
         * The request was made and the server responded with a
         * status code that falls out of the range of 2xx
         */
        console.log(error.response.data);
        console.log(error.response.status);
        console.log(error.response.headers);
    } else if (error.request) {
        /*
         * The request was made but no response was received, `error.request`
         * is an instance of XMLHttpRequest in the browser and an instance
         * of http.ClientRequest in Node.js
         */
        console.log(error.request);
    } else {
        // Something happened in setting up the request and triggered an Error
        console.log('Error', error.message);
    }
    console.log(error);
}
They have some more examples here. In some other HttpClients, they have the option to always return a full http response. I don't see that option on Axios, but it might just be hidden in their documentation.