It's definitely not about the environment. Yes, there are browser inconsistencies, but for the most part, this is not a major issue today. The problem of js apps is handling asynchronicity, having to fetch data from the server and managing UI state and in the case of an SPA, having to take care of routing, permissions, timeouts, errors etc. When you sum it all up, you end up with a front-end that mostly needs to replicate all logic from the server, apart from running the sql queries.
On the other side, when you render on the server, everything is already there. It's easy to write logic when the data you need is an sql query away and all you need to do is spit out some html. What gets a bit complex is keeping working state between pages and this usually requires handling sessions, cookies etc.
Well, the environment isn't the only factor, but it's the biggest one. You have to take care of routing, permissions, timeouts, errors etc on the server as well. Data being "an sql query away" on the server is similar to being "an AJAX call away" on the client.
If you have a UI, you're going to have to manage UI state somewhere. Even if you have no client-side code, you've marshal that state into strings to send to the client, and then parse it all out again on the server when you get the next request. Remember hidden form fields? Heck, this very site has some pretty interesting history about attempts to solve that problem elegantly. It was a PITA.
Ultimately, none of that has to do with Javascript; it's inherent in a client-server architecture. The one thing that is specific to JS is asynchronicity. If you don't like that, ok, fair enough, but that's a matter of taste. It has advantages as well as disadvantages, and with Promises and async/await, it's actually pretty elegant. It's certainly not the case that asynchronicity makes you write shit code.
Your first paragraph is exactly my point. Because in an SPA, you have to handle it in both places, not only on the server. That makes it twice as hard as an old fashioned server-side mvc page.
> Data being "an sql query away" on the server is similar to being "an AJAX call away" on the client.
This is a bit disingenuous, since sql calls in most back end languages are synchronous. Also an ajax call needs to update the UI to tell the user something is happening. This might mean disabling controls, showing spinners etc.
All of this goes away if you only use server-side rendering since form requests are natively handled by the browser.
> It's certainly not the case that asynchronicity makes you write shit code.
But it expands the number of cases you have to deal in the UI, to show all the loading states, error states etc.
On the other side, when you render on the server, everything is already there. It's easy to write logic when the data you need is an sql query away and all you need to do is spit out some html. What gets a bit complex is keeping working state between pages and this usually requires handling sessions, cookies etc.