Better code for error handling in NodeJs and Express
What you first need to do is convert all non-promise/callback-based operations into promises, or wrap them in one. This way, you can write seamless promise-based code. For instance, convert bcrypt.compare() into:

const bcryptCompare = (pw1, pw2) =&gt; {
  return new Promise((resolve, reject) =&gt; {
    bcrypt.compare(pw1, pw2, (error, result) =&gt; {
      if(error) reject(error)
      else resolve(result)
    })
  })
}


There are libraries that do this for you, and newer versions of libraries usually support promise-based versions of their APIs. But if you ever need to craft this yourself, this is how it's generally done.

Next, I would separate express from this logic. This way, you're not constrained with Express. Instead of accessing request, pass only the needed information to your function. Instead of immediately sending a response, consider throwing error objects instead. This way, you can defer your response logic to your routing logic.

Next, you could turn to async/await for cleaner code. It's just syntax sugar on top of Promises, allowing asynchronous code to look synchronous.

// Custom error class to house additional info. Also used for determining the
// correct response later.
class PasswordMismatch extends Error {
  constructor(message, user){
    this.message = 'Neue Passwörter stimmen nicht überein!'
    this.user = user
  }
}

class UserDoesNotExist extends Error {...}
class PasswordIncorrect extends Error {...}

// More custom error classes here

// Our logic, which knows nothing about express and only requires a few
// pieces of information for our operation.
const changePassword = async (username, oldPw, newPw, newPw2) =&gt; {

  // Throwing an error inside an async function rejects the promise it returns
  if (newPw !== newPw2) {
    throw new PasswordMismatch('Neue Passwörter stimmen nicht überein!', user)
  }

  const results = await SQL.getUserFromDB(username)

  if (!results) {
    throw new UserDoesNotExist('...', user)
  }

  const result = bcryptCompare(oldPw, results[0].password)

  if(result !== true) {
    throw new PasswordIncorrect('...', user)
  }

  try {
    SQL.changeUserPassword(username, newPw)
  } catch(e) {
    throw new PasswordChangeFailed('...', user)
  }
}

// In your express router
app.post('route', async (request, response) =&gt; {
  // Gather the necessary data from the request
  const username = request.session.username
  const oldPw = request.body.oldPw
  const newPw = request.body.newPw
  const newPw2 = request.body.newPw2

  try {
    await changePassword(username, oldPw, newPw, newPw2)

    // If the operation didn't throw, then we're good. Happy path.
    response.render('pages/changePassword', {
      user: username,
      text: 'Passwort erfolgreich geändert.'
    });
  } catch (e) {
    // If any of the operations failed, then we determine the right response

    // In this case, we used a custom error. We can get the data from it.
    // You can have more of these for the other custom classes.
    if(error instanceof PasswordConfirmError){
      response.render('pages/changePassword', {
        user: error.user,
        text: error.message
      });

    } else {
      // Generic 500 error, if all else fails.
    }
  }
})