I’m doing everything wrong
Today I learned...
I’ve been writing webapps completely wrong.
Putting secured API interactions and database requests in the hands of the client’s browser is a bad practice for multiple reasons. First, it’s extremely insecure. Anyone with Chrome or Firefox can use dev tools to exploit API keys and read and write to a database. Your top priority may not be security, but still: this is extremely dangerous. Even if you build practice apps with frontend interactions, never push one out as a consumer-facing app.
Second, it creates a bad user experience. Depending on the client’s browser, operating system, memory, connection, ect., fetching resources from within the browser can dramatically slow down the web page and the computer. If you absolutely have to perform requests in the frontend, at least generate a loading symbol so that the user can tell what’s happening.
So, now I’ve got to go back and rewrite all my apps to use NodeJS servers. Blech. Take a word of advice- don’t cut corners. Use the right practices!
In the interest of maybe improving the situation in general: Where do you think a warning should have been so that you would have seen it earlier?
I.e. which resources are teaching how to make API calls to other services from the client without mentioning that this only applies to unauthenticated endpoints?
I haven't looked into it very deeply, but from what I can tell, you can use Firebase to build client-consumable APIs, which then are fine to hit directly from the frontend. You will have to set appropriate CORS permissions in that case (see below), though, and you should still only transmit the data that the client actually needs to display. Doing the processing in a separate proxy may be cheaper in terms of hosting costs, but I haven't looked into the pricing enough there. The apps I worked on were generally self-hosted, so we fortunately didn't have to deal with those questions.
All that said:
Calling third-party APIs with authentication from the client actually mostly shouldn't work in the first place, due to CORS security.
Modern browsers won't issue requests to other domains unless the target service is properly configured to give them permission for it, which isn't the default. Since you haven't run into that problem, I suspect you haven't deployed any web app/API key like that online yet.
If a service is CORS-permissive but requires a secret API key, then in my opinion they're partly to blame for any misuse that happens. There's usually a warning not to publish them next to any keys/tokens, but that doesn't mean they should make it easier to have them in the client than to secure them properly.
Occasionally, a (usually very new) developer sets up an open CORS proxy (without a domain whitelist). These are often misused by malware, since they disable one of the fundamental building blocks of web security that a lot of other safety mechanisms build on.
I'm mentioning it here because that's probably the most important point to be wary of when creating a backend service for your projects: Always limit it so only the same domain (default) or a specific set of trusted websites can make requests.
Referer whitelist causes issues with certain privacy add-ons, so unless your service is available without TLS (CORS doesn't always apply over plain HTTP.), it's better to avoid using that feature.
Ali Dhuniya It's good. The server software there does everything I mentioned and it's definitely enough for relatively light apps. It's also good to see that best practices are the default and it doesn't support allowing all ('any') domains without changing its code.
When you build heavier apps, for example if you need dynamic <meta> tags for link previews to your pages from social media or have the user log in to your website directly or anything like that, then you'll still have to build a custom backend, though. An exception would be OAuth (i.e. "Log in with Twitter" and the like), which will still work with that server in many cases.
Personally, I would likely build the server in something more lightweight than Node.js, but I don't think it's much of a difference in practical terms, if any.