Forgive me if I misunderstood your question or if you know most of this already, but it sounds like you're asking about the distinction between data being displayed by an application and the application itself. Or more generally, the difference between a single page application and a server-side rendered page.
In a server-side rendering application, every navigation loads a new URL from the server (or cache). The server uses authorization information from the request to fetch the appropriate data, insert it into a templated HTML page and then returns that page. According to the client, the data the page displays and the page itself are a single entity. If the data can't be fetched, a different page is returned indicating that the user doesn't have access.
In a single page application, every possible 'page' you can navigate to is downloaded once and they are all devoid of any actual data. Instead, these pages are a container for data. The behavior of these pages make API requests to the server for the data to display.
The application itself isn't protected - anyone can navigate to it or any of its pages. However, the data is protected. If the user doesn't have access to the data requested by an API call, a 401 status code is returned with JSON error body.
Therefore, there has to be a distinction between the client and server-side routes. You have already noticed that client-side routes don't actually make an HTTP request - this is intentional.
If you are serving the application and the API from the same application, it is a good idea to prefix all of your API routes with something like /api
. For example, the client-side route is /heroes
and the server-side route is /api/heroes
. This also allows a mobile application (or any other non-browser application) to consume your API; they won't want the HTML because they have their own rendering behavior.
As Günter said, if there a page that is useless without access to the API data, punt them back to a login page. There are two scenarios where you don't have access to API data: you don't have an access token at all and your access token is expired.
Make the API request when you navigate to the /heroes
client-side route. If you don't have an access token, punt them to a login page. If the request yields a 401, punt them back to a login page. If you get back 200, then operate as normal.
One place where client-side routing gets in the way is when trying to type a client-side route URL into a browser (as opposed to programmatically navigating to it). There are different strategies to side-step this, here's one: https://github.com/stablekernel/aqueduct/issues/274.