16. Example [nuxt-13]: Checking the navigation of [nuxt-12]
In this example, we are focusing on the navigation of [nuxt-12]. We did not do this in [nuxt-12] because controlling navigation would have added complexity to an already complex example.
Objective: we want the user to be able to perform only authorized actions:
- if the JSON session has not started, then only the URL [/] is allowed;
- if the JSON session has started but the user is not authenticated, then only the URL [/authentication] is allowed;
- if the JSON session has started and the user is authenticated, then only the URLs [/get-admindata, /end-session] are allowed;
- when the current routing target is not authorized, a redirect to an authorized URL will be performed;
The [nuxt-13] example is initially obtained by copying the [nuxt-12] example:

The changes will be made in the routing folder [middleware].
16.1. Routing for the [nuxt] application
The application’s routing is configured as follows in the [nuxt.config] file:
// router
router: {
// application URL root
base: '/nuxt-13/',
// routing middleware
middleware: ['routing']
},
- line 6: the application's routing is controlled by the [middleware/routing] file;
The [middleware/routing] file is as follows:
/* eslint-disable no-console */
// import server and client middleware
import serverRouting from './server/routing'
import clientRouting from './client/routing'
export default function(context) {
// Who is executing this code?
console.log('[middleware], process.server', process.server, ', process.client=', process.client)
if (process.server) {
// server routing
serverRouting(context)
} else {
// client routing
clientRouting(context)
}
}
- lines 10–16: client and server [nuxt] routing are handled differently. This is a major point of difference between them;
- line 4: server routing is implemented by the script [middleware/server/routing];
- line 5: client routing is implemented by the [middleware/client/routing] script;
16.2. [nuxt] client routing
Client-side routing [nuxt] remains the same as it was in [nuxt-12]:
/* eslint-disable no-console */
export default function(context) {
// Who is running this code?
console.log('[client middleware], process.server', process.server, ', process.client=', process.client)
// managing the PHP session cookie in the browser
// The browser's PHP session cookie must match the one found in the Nuxt session
// the [end-session] action receives a new PHP cookie (both server and Nuxt client)
// if the server receives it, the client must pass it to the browser
// for its own communication with the PHP server
// we are here in client-side routing
// We retrieve the PHP session cookie
const phpSessionCookie = context.store.state.phpSessionCookie
if (phpSessionCookie) {
// if it exists, we assign the PHP session cookie to the browser
document.cookie = phpSessionCookie
}
...
}
To prevent the client from accessing unauthorized routes, we will simply offer only the authorized routes in the client navigation menu. The [components/navigation] component becomes the following:
<template>
<!-- Bootstrap menu with three options -->
<b-nav vertical>
<b-nav-item v-if="$store.state.jsonSessionStarted && !$store.state.userAuthenticated" to="/authentication" exact exact-active-class="active">
Authentication
</b-nav-item>
<b-nav-item
v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated && !$store.state.adminData"
to="/get-admindata"
exact
exact-active-class="active"
>
AdminData Request
</b-nav-item>
<b-nav-item v-if="$store.state.jsonSessionStarted && $store.state.userAuthenticated" to="/end-session" exact exact-active-class="active">
End Tax Session
</b-nav-item>
</b-nav>
</template>
- line 4: the [Authentication] option is only available if the JSON session has started but the user is not authenticated. If the JSON session has not started or the user is already authenticated, then the option is not available;
- Lines 7–11: The [AdminData Request] option is only available if the JSON session has started, the user is authenticated, and the [AdminData] has not yet been retrieved. If any of these three conditions is not met (jSON session not started, user not authenticated, or [AdminData] data already retrieved), the option is not offered;
- line 15: the [End Tax Session] option is available as soon as the JSON session has started and the user is authenticated; otherwise, it is not available;
16.3. Server Routing [Nuxt]
Server routing is generally more complex than client-side routing because the user can type any URL into their browser’s address bar. We can let that happen (after all, the user isn’t supposed to do that) or try to control it. That’s what we’ll do here, for the sake of this example, because in the case of the [nuxt-12] application, we can easily do without it since the tax calculation server is well-protected against these manually entered URLs and knows how to send the appropriate error messages. We saw this in [next-12], where there was no routing control.
Routing on a [nuxt] server is very different from that on a [nuxt] client in terms of redirection:
- when a [nuxt] server is redirected, it sends a redirect request to the client browser with the redirect target. The browser then makes a new request to the [nuxt] server, asking for the target that was sent to it. It’s as if the user had manually typed in the URL of the redirect target: the entire [nuxt] application restarts, and with it, its entire lifecycle (server plugins, store, server routing, pages);
- when a [nuxt] client is redirected, none of this happens. There is simply a page change, the same as would have occurred if the user had clicked a link leading to the redirect target. The lifecycle is then different (client-side routing, rendering the route’s target);
For this reason, it is preferable to separate client-side routing from server-side routing, even though the two code bases may appear similar.
The server routing script [middleware/server/routing] will be as follows:
/* eslint-disable no-console */
export default function(context) {
// Who is executing this code?
console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
// retrieve some information from the [nuxt] store
const store = context.store
// where are we coming from?
const from = store.state.from || 'nowhere'
...
}
- In client-side routing, the routing function receives the context [context] with the property [context.from], which is the route of the page we came from. The route we are going to is obtained via [context.route];
- In server-side routing, the routing function receives the context [context] without the [context.from] property. Server-side routing only occurs when a URL is manually requested from the [nuxt] server. We know that the entire [nuxt] application is then reset. It’s as if we were starting from scratch, so there is no concept of a “previous page”;
- thanks to the [nuxt] session, we know that the server can retrieve this session and thus avoid starting from scratch. It is therefore in this [nuxt] session, and more specifically in the session’s store, that we will store the name of the last page displayed by the client browser before a URL is requested from the [nuxt] server;
- Lines 7–9: We retrieve the name of the last page displayed by the client browser. When the application starts, this information [from] does not exist in the store. We then assign the name [nowhere] to the variable [from];
In order for the [nuxt] server to retrieve the name of the last page displayed by the client browser from the store, the [nuxt] client must also put this information into the store. The [nuxt] client routing script is therefore completed as follows:
/* eslint-disable no-console */
export default function(context) {
// Who is executing this code?
console.log('[client middleware], process.server', process.server, ', process.client=', process.client)
// managing the PHP session cookie in the browser
// The browser's PHP session cookie must match the one found in the Nuxt session
// The [end-session] action receives a new PHP cookie (both server and Nuxt client)
// if the server receives it, the client must pass it to the browser
// for its own communication with the PHP server
// we are here in client-side routing
// We retrieve the PHP session cookie
const phpSessionCookie = context.store.state.phpSessionCookie
if (phpSessionCookie) {
// if it exists, we assign the PHP session cookie to the browser
document.cookie = phpSessionCookie
}
// we put the name of the page we're going to in the session - no server redirection
context.store.commit('replace', { serverRedirection: false, from: context.route.name })
// save the store to the [nuxt] session
const session = context.app.$session()
session.value.store = context.store.state
session.save(context)
}
- Lines 19–24 are added;
- Line 20: We store the name of the page [context.route.name] that will be displayed in the store, which will then serve as the page we came from during the next routing step. Furthermore, we will see that in the [nuxt] server’s routing, it needs to know if the current routing stems from a previous redirection by the [nuxt] server. Here, that is not the case, so we set the [serverRedirection] property to [false];
- lines 22–24: the state of the store is placed in the [nuxt] session (line 23), then the [nuxt] session is saved in a cookie (line 24), which in turn will be saved in the [nuxt] client’s browser;
Let’s go back to the [nuxt] server routing script:
/* eslint-disable no-console */
export default function(context) {
// Who is executing this code?
console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
// we retrieve some information from the [nuxt] store
const store = context.store
// where are we coming from?
const from = store.state.from || 'nowhere'
// where are we going?
const to = context.route.name
// possible redirection
let redirect = ''
// routing complete
let done = false
// Are we already in a [nuxt] server redirect?
if (store.state.serverRedirection) {
// nothing to do
done = true
}
// Is this a page reload?
if (to === from) {
// do nothing
done = true
}
// Check server navigation [nuxt]
// based on client-side navigation in the [navigation] component
// case where the PHP session has not started
if (!done && !store.state.jsonSessionStarted && to !== 'index') {
// Redirect
redirect = 'index'
// task completed
done = true
}
// if the user is not authenticated
if (!done && store.state.jsonSessionStarted && !store.state.userAuthenticated && to !== 'authentication') {
// redirection
redirect = from
// task completed
done = true
}
// If the user has been authenticated
if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && to !== 'get-admindata' && to !== 'fin-session') {
// stay on the same page
redirection = from
// task completed
done = true
}
// if [adminData] has been obtained
if (!done && store.state.jsonSessionStarted && store.state.userAuthenticated && store.state.adminData && to !== 'end-session') {
// stay on the same page
redirect = from
// task completed
done = true
}
// all checks have been performed ---------------------
// Redirect?
if (redirect) {
// we note the redirection in the store
store.commit('replace', { serverRedirection: true })
} else {
// no redirect
store.commit('replace', { serverRedirection: false, from: to })
}
// save the store to the [nuxt] session
const session = context.app.$session()
session.value.store = store.state
session.save(context)
// perform any necessary redirection
if (redirect) {
context.redirect({ name: redirection })
}
}
- lines 6–9: retrieve the value of [from] from the [nuxt] server store;
- line 11: we note the target of the current route;
- line 13: routing may result in a redirect of the client browser. [redirection] will be the target of this redirect;
- line 15: [done] set to [true] indicates that routing is complete;
- lines 17–21: First, we check if the current routing resulted from a redirect request sent to the client browser. This information is stored in the [serverRedirection] property of the store. If this property is true, then the [nuxt] server sent a redirect to the client browser during the previous request to the [nuxt] server. In this case, no routing is required. During the previous request, the [nuxt] server router decided that the client browser should be redirected. This decision does not need to be overridden by a new routing;
- lines 23–27: we check if the current routing is a page reload. If so, we let it proceed;
- Starting on line 29, we resume the rules applied in the [navigation] component of the [nuxt] client (see previous paragraph);
- lines 32–38: we handle the case where the JSON session has not started and the routing target is not the [index] page. In this case, we redirect the client browser to the [index] page;
- lines 40–46: we handle the case where the JSON session has started, the user is not authenticated, and the current routing target is not the [authentication] page. In this case, we reject the routing and remain where we were;
- lines 48–54: Handles the case where the JSON session has started, the user is authenticated, and the current routing target is neither the [get-admindata] page nor the [end-session] page, which are then the only possible destinations. In this case, the requested routing is rejected, and we return to where we were previously;
- lines 56–62: we handle the case where [adminData] has been obtained. In this case, there is only one possible routing target: the [fin-session] page. If that was not the page requested, we reject the routing and return to where we were previously;
- lines 64–72: if a redirect occurred, we record it in the [nuxt] server store: [serverRedirection: true]. Note that we do not assign a value to the [from] property of the store. The reason is that there will be a redirect from the client browser, and we saw that in this case, there was no routing (lines 17–20) and the [from] property of the store is not used;
- Lines 66–69: If there is no redirect, this is also noted in the [nuxt] server store: [serverRedirection: false]. Additionally, the current routing will display the [to] page, which for the next request (client or [nuxt] server) will become the previous page. This is why we write [from: to];
- lines 73–76: We save the store to the [nuxt] session, which is itself saved in a cookie;
- lines 77–80: if [redirection] is not empty, then we instruct the browser to redirect. Otherwise (not shown here), the [nuxt] server’s lifecycle will continue: the [to] page will be processed by the [nuxt] server and sent to the [nuxt] client’s browser along with the [nuxt] session cookie;
The routing chosen here for the [nuxt] server is arbitrary. We could have chosen another one or, as mentioned, not used routing at all. The one chosen above has the advantage of always keeping the application in a stable state regardless of the URL requested by the user.
We can improve this slightly when the page that is ultimately loaded is the original page. There are two cases:
- the user has triggered a page reload (to===from);
- there are redirects to the original page (redirection===from);
In both cases, the original page will be executed again with its asynchronous call to the tax calculation server. Let’s take an example. If, once authenticated, the user reloads the page (F5). In this case, in the routing above, we have: [to]=[from]=[authentication]. There is no redirection. The [to=authentication] page will be executed by the [nuxt] server. If we do nothing, the [asyncData] function will run again. This is unnecessary since authentication has already been performed.
We can improve this by slightly modifying the [authentication] page:
// asynchronous data
async asyncData(context) {
// log
console.log('[authentication asyncData started]')
// we don't do things twice if the page has already been requested
if (process.server && context.store.state.userAuthenticated) {
console.log('[asyncData authentication canceled]')
return { result: '[success]' }
}
// client [nuxt]
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
// authenticate with the server
...
- lines 6-9: if the page is being served by the [nuxt] server and we find in the store that authentication has already been performed, then we return the desired result directly (line 8);
We do the same for all pages:
Page [index]:
// asynchronous data
async asyncData(context) {
// log
console.log('[index asyncData started]')
// we don't do things twice if the page has already been requested
if (process.server && context.store.state.jsonSessionStarted) {
console.log('[index asyncData canceled]')
return { result: '[success]' }
}
try {
...
Page [get-admindata]
// asynchronous data
async asyncData(context) {
// log
console.log('[get-admindata asyncData started]')
// We don't do things twice if the page has already been requested
if (process.server && context.store.state.adminData) {
console.log('[get-admindata asyncData canceled]')
return { result: context.store.state.adminData }
}
// client
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
...
Page [end-session]
// asynchronous data
async asyncData(context) {
// log
console.log('[end-session asyncData started]')
// we don't do things twice if the page has already been requested
if (process.server && context.store.state.jsonSessionStarted && !context.store.state.userAuthenticated) {
console.log('[end-session asyncData canceled]')
return { result: "[success]. The JSON session remains initialized, but you are no longer authenticated." }
}
// [nuxt] client case
if (process.client) {
// start waiting
context.app.$eventBus().$emit('loading', true)
// no error
context.app.$eventBus().$emit('errorLoading', false)
}
try {
16.4. Execution
To run this example, make sure to delete the [nuxt] session cookie and the PHP cookie from the browser running the [nuxt] client before execution to start with a clean slate. Below is an example using the Chrome browser:

16.5. Conclusion
Routing on the [nuxt] server is complex because you must anticipate every URL the user might type manually. This is a textbook example. A [nuxt] application is not intended to be used this way. Once the [index] page is served by the [nuxt] server’s router, subsequent requests made to the server could be redirected to an error page.
In the specific case of our [nuxt-13] example, [nuxt] server routing was unnecessary. The default behavior (essentially no routing) in the [nuxt-12] example worked just fine.