These are the 4 things that I would have done differently. I would have...
 ...built out the routes first.
It is true that generally you want to get off the ground fast, and build your app feature by feature, but routes might just be an exception. Since they are confusing, unless you are deeply familiar, you should try to get them done all at once asap.
 ...set up authentication first.
Why? Without authentication, you'll be building your app without login guards etc... Building your app without that check is very smooth. It gives a false sense of progress. Once you have authentication, suddenly things start breaking left and right and you're in chaos trying to contain the damage. It's like a dam broke and water flooded in. You suddenly have to postpone everything and work out these problems, fixing one bug after another, trying to determine which line of code caused the situation. Figuring one out doesn't guarantee you figured the rest out!
Building an app is is generally an iterative process, but for certain things you have to do it right from the get go, since other things depend on it. This might just be one of these things.
So build out authentication and write your code in a way that accounts for both states of having access and no access early on.. Why? If you just assume the user always has access, then you'll have to fix that code, and not merely adding more code to address the second situation.
 ...learned the conventional way of doing features.
As I mentioned above, authentication is complicated. There are many decisions to make, many of them will work, but how likely are that they work WELL? Login is so universal that theres no excuse not to really master its best practices.
Learning coding patterns and best practices cannot be overemphasized. As a naive deer in the headlight, I liked to challenge myself to write my code without much reference and only look up solutions when stuck. I didn't want to look at similar apps' code. They seem like copying and cheating. But in retrospect, this is a mistake.
- a) trying to reinvent the wheel on anything other than a mini-project is not appropriate. It introduces too much risk of delay and overwhelm. Even if it works, it might be a very bad way that causes more headaches down the road, and possibly dead ends.
- b) best practices and conventional practices are good in ways you don't completely understand. It's beyond your ability to understand for now. They help prevent the headaches and dead-ends I just mentioned above. Thus, as a newbie, you should trust them more than your own solution. It is critical that you do research on similar apps and stick to these conventions. This might seem like you are not learning much, your project won't be a copy and paste exercise. There will be slight differences where you have to use your brain. There will be plenty of learning along the way!
 ...controlled my ambitions
The way to accomplish any big goal is to break it down to digestible chunks. If your goal is to be a good programmer, what are the digestible chunks? You should aim to a) build projects that are, not too easy, not too hard b) build them frequently.
I believe consistent efforts like this are more sustainable than occasional heroic bursts of hustles that ultimately induce burnout.
Big projects is a time ripe for overwhelm and time delay. All projects have these risks, but you should aim to minimize them. Thus, at the level of building a big complicated app, you want to be ultra-conservative with, well, essentially everything. Don't reach for the moon, as poetic as it sounds. You don't want to fall in love with a house without considering your budget. Your budget in this case is time and ability.
Specifically, what should you do instead? Do something you already know how to do. And follow best practices. If all you got is a hammer, find some nails. Make the app fit your abilities. Don't do a project that you know explicitly has so and so features that you have no idea how to do yet.
Don't be wild. Have a process. Stick to it. Do things in the right order. Show restraint.