Skip to content

11. Example [nuxt-08]: routing middleware

In this example, we introduce the concept of routing middleware, scripts executed whenever a route changes.

The example [nuxt-08] is initially obtained by copying the project [nuxt-01]:

Image

The routing middleware must be in a folder named [middleware] [2]. There may be two-level routing:

  • a routing applied to each navigation. This is then declared in the [nuxt.config.js] file;
  • a routing applied to a specific page, when that page is the target of the routing. This routing is then declared in that target page;

11.1. General Routing

The [middleware / routing.js] file will handle general routing. It is declared as follows in the [nuxt.config.js] file:


  router: {
    base: '/nuxt-08/',
    middleware: ['routing']
},

General routing middleware is a property of the router (line 1). There can be multiple routing middleware components. This is why, on line 3, the value of the [middleware] property is an array. Note that we do not use a path to specify the middleware. It will be automatically searched for in the [middleware] folder of the project;

The [routing] middleware only logs here:


/* eslint-disable no-undef */
/* eslint-disable no-console */
export default function(...args) {
  // who executes this code?
  console.log('[routing], process.server=', process.server, 'process.client=', process.client)
  const who = process.server ? 'server' : 'client'
  const routing = '[routing ' + who + ']'
  // number of arguments
  console.log(routing + ', il y a', args.length, 'argument(s)')
 
  // 1st argument
  const context = args[0]
  // key context
  dumpkeys(routing + ', context', context)
  // the application
  dumpkeys(routing + ', context.app', context.app)
  // the road
  dumpkeys(routing + ', context.route', context.route)
  console.log(routing + ', context.route=', context.route)
  // the router
  dumpkeys(routing + ', context.app.router', context.app.router)
  // on router.options.routes
  dumpkeys(routing + ', context.app.router.options.routes', context.app.router.options.routes)
  console.log(routing + ', context.app.router.options.routes=', context.app.router.options.routes)
}
 
function dumpkeys(message, object) {
  // list of [object] keys
  const ligne = 'Liste des clés [' + message + ']'
  console.log(ligne)
  // kEY LIST
  if (object) {
    console.log(Object.keys(object))
  }
}
  • line 3: we will see that the middleware receives an argument: the executor context (server or client);
  • lines 4–25: we display the properties of various objects to determine what is usable. We will find that the middleware context is nearly identical to the plugin context;

11.2. Routing for a specific page

We want to control how we get to the page [index]. To do this, we need to introduce the property [middleware] into this page [index]:


<script>
/* eslint-disable no-undef */
/* eslint-disable no-console */
/* eslint-disable nuxt/no-env-in-hooks */
 
import Navigation from '@/components/navigation'
import Layout from '@/components/layout'
 
export default {
  name: 'Home',
  // components used
  components: {
    Layout,
    Navigation
  },
  // life cycle
  beforeCreate() {
    // client and server
    console.log('[home beforeCreate]')
  },
  created() {
    // client and server
    console.log('[home created]')
  },
  beforeMount() {
    // customer only
    console.log('[home beforeMount]')
  },
  mounted() {
    // customer only
    console.log('[home mounted]')
  },
  // routing
  middleware: ['index-routing']
}
</script>
  • line 34: the [middleware] property lists the scripts to be executed whenever the next page displayed is the [index] page. Again, these scripts will be searched for in the [middleware] folder of the project;

The [index-routing] middleware is as follows:


/* eslint-disable no-undef */
/* eslint-disable no-console */
export default function(...args) {
  // who executes this code?
  console.log('[index-routing], process.server=', process.server, 'process.client=', process.client)
  const who = process.server ? 'server' : 'client'
  const indexRouting = '[index-routing ' + who + ']'
  // number of arguments
  console.log(indexRouting + ', il y a', args.length, 'argument(s)')
 
  // 1st argument
  const context = args[0]
  // key context
  dumpkeys(indexRouting + ', context', context)
  // the application
  dumpkeys(indexRouting + ', context.app', context.app)
  // the road
  dumpkeys(indexRouting + ', context.route', context.route)
  console.log(indexRouting + ', context.route=', context.route)
  // the router
  dumpkeys(indexRouting + ', context.app.router', context.app.router)
  // on router.options.routes
  dumpkeys(indexRouting + ', context.app.router.options.routes', context.app.router.options.routes)
  console.log(indexRouting + ', context.app.router.options.routes=', context.app.router.options.routes)
  // where do we come from?
  if (context.from) {
    console.log('from=', context.from)
  }
}
 
function dumpkeys(message, object) {
  // list of [object] keys
  const ligne = 'Liste des clés [' + message + ']'
  console.log(ligne)
  // kEY LIST
  if (object) {
    console.log(Object.keys(object))
  }
}

The code for [index-routing] is identical to that of [routing] and produces the same results. What interests us is seeing when these two middlewares are executed.

11.3. Project Execution

We run the project. The logs are as follows:

The script [routing] is executed first by the server:

[routing], process.server= true process.client= false
[routing server], il y a 1 argument(s)
Liste des clés [[routing server], context]
[ 'isStatic',
   'isDev',
   'isHMR',
   'app',
   'payload',
   'error',
   'base',
   'env',
   'req',
   'res',
   'ssrContext',
   'redirect',
   'beforeNuxtRender',
   'route',
   'next',
   '_redirected',
   '_errored',
   'params',
   'query',
   '$axios' ]
Liste des clés [[routing server], context.app]
[ 'router',
   'nuxt',
   'head',
   'render',
   'data',
   'beforeCreate',
   'created',
   'mounted',
   'watch',
   'computed',
   'methods',
   'components',
   'context',
   '$axios' ]
Liste des clés [[routing server], context.route]
[ 'name',
   'meta',
   'path',
   'hash',
   'query',
   'params',
   'fullPath',
   'matched' ]
[routing server], context.route= { name: 'index',
  meta: [ {} ],
  path: '/',
  hash: '',
  query: {},
  params: {},
  fullPath: '/',
  matched:
   [ { path: '',
       regex: /^(?:\/(?=$))?$/i,
       components: [Object],
       instances: {},
       name: 'index',
       parent: undefined,
       matchAs: undefined,
       redirect: undefined,
       beforeEnter: undefined,
       meta: {},
       props: {} } ] }
Liste des clés [[routing server], context.app.router]
[ 'app',
   'apps',
   'options',
   'beforeHooks',
   'resolveHooks',
   'afterHooks',
   'matcher',
   'fallback',
   'mode',
   'history' ]
Liste des clés [[routing server], context.app.router.options.routes]
[ '0', '1', '2' ]
[routing server], context.app.router.options.routes= [ { path: '/page1',
    component: [Function: _61cefe10],
    name: 'page1' },
  { path: '/page2',
    component: [Function: _61dd1591],
    name: 'page2' },
  { path: '/', component: [Function: _00d5e140], name: 'index' } ]

This is the same result we obtained with the plugins.

  • line 15: the [redirect] property is often used in middleware: it allows you to change the target of the current routing;

Then, because the page to be displayed is [index], the server executes the [index-routing] script and displays the following logs:

[index-routing], process.server= true process.client= false
[index-routing server], il y a 1 argument(s)
Liste des clés [[index-routing server], context]
[ 'isStatic',
   'isDev',
   'isHMR',
   'app',
   'payload',
   'error',
   'base',
   'env',
   'req',
   'res',
   'ssrContext',
   'redirect',
   'beforeNuxtRender',
   'route',
   'next',
   '_redirected',
   '_errored',
   'params',
   'query',
   '$axios' ]
Liste des clés [[index-routing server], context.app]
[ 'router',
   'nuxt',
   'head',
   'render',
   'data',
   'beforeCreate',
   'created',
   'mounted',
   'watch',
   'computed',
   'methods',
   'components',
   'context',
   '$axios' ]
Liste des clés [[index-routing server], context.route]
[ 'name',
   'meta',
   'path',
   'hash',
   'query',
   'params',
   'fullPath',
   'matched' ]
[index-routing server], context.route= { name: 'index',
  meta: [ {} ],
  path: '/',
  hash: '',
  query: {},
  params: {},
  fullPath: '/',
  matched:
   [ { path: '',
       regex: /^(?:\/(?=$))?$/i,
       components: [Object],
       instances: {},
       name: 'index',
       parent: undefined,
       matchAs: undefined,
       redirect: undefined,
       beforeEnter: undefined,
       meta: {},
       props: {} } ] }
Liste des clés [[index-routing server], context.app.router]
[ 'app',
   'apps',
   'options',
   'beforeHooks',
   'resolveHooks',
   'afterHooks',
   'matcher',
   'fallback',
   'mode',
   'history' ]
Liste des clés [[index-routing server], context.app.router.options.routes]
[ '0', '1', '2' ]
[index-routing server], context.app.router.options.routes= [ { path: '/page1',
    component: [Function: _61cefe10],
    name: 'page1' },
  { path: '/page2',
    component: [Function: _61dd1591],
    name: 'page2' },
  { path: '/', component: [Function: _00d5e140], name: 'index' } ]

The results obtained with the [index-routing] script are similar to those obtained with the [routing] script.

Once the [index] page is received by the client browser, the client scripts take over. The logs become as follows:

1
2
3
4
[home beforeCreate]
[home created]
[home beforeMount]
[home mounted]

We can see, therefore, that when the application starts, the client does not execute any middleware. This means that this will occur every time the user initiates a request to the server. Middleware is executed by the client only during a navigation within the client. For example, let’s navigate to the [page1] page (we are currently on the [index] page) using the [Page 1] link. The logs are then as follows:

[routing], process.server= false process.client= true
[routing client], il y a 1 argument(s)
Liste des clés [[routing client], context]
(18) ["isStatic", "isDev", "isHMR", "app", "payload", "error", "base", "env", "redirect", "nuxtState", "route", "next", "_redirected", "_errored", "params", "query", "$axios", "from"]
Liste des clés [[routing client], context.app]
(14) ["router", "nuxt", "head", "render", "data", "beforeCreate", "created", "mounted", "watch", "computed", "methods", "components", "context", "$axios"]
Liste des clés [[routing client], context.route]
(8) ["name", "meta", "path", "hash", "query", "params", "fullPath", "matched"]
[routing client], context.route= {name: "page1", meta: Array(1), path: "/page1", hash: "", query: {}, }
Liste des clés [[routing client], context.app.router]
(10) ["app", "apps", "options", "beforeHooks", "resolveHooks", "afterHooks", "matcher", "fallback", "mode", "history"]
Liste des clés [[routing client], context.app.router.options.routes]
(3) ["0", "1", "2"]
[routing client], context.app.router.options.routes= (3) [{}, {}, {}]0: {path: "/page1", name: "page1", component: ƒ}1: {path: "/page2", name: "page2", component: ƒ}2: {path: "/", name: "index", component: ƒ}length: 3__proto__: Array(0)
[page1 beforeCreate]
[page1 created]
[page1 beforeMount]
[page1 mounted]
  • line 2: the [routing] middleware is executed by the client;
  • line 4: note the [from] property: this is the route we came from;
  • line 9: [context.route] is the route we are going to;
  • lines 15–18: display of the [page1] page;

Now let’s return to the page [index] via the link [Home]. The logs are then as follows:

[routing], process.server= false process.client= true
[routing client], il y a 1 argument(s)
Liste des clés [[routing client], context]
(18) ["isStatic", "isDev", "isHMR", "app", "payload", "error", "base", "env", "redirect", "nuxtState", "route", "next", "_redirected", "_errored", "params", "query", "$axios", "from"]
Liste des clés [[routing client], context.app]
(14) ["router", "nuxt", "head", "render", "data", "beforeCreate", "created", "mounted", "watch", "computed", "methods", "components", "context", "$axios"]
Liste des clés [[routing client], context.route]
(8) ["name", "meta", "path", "hash", "query", "params", "fullPath", "matched"]
[routing client], context.route= {name: "index", meta: Array(1), path: "/", hash: "", query: {}, }
Liste des clés [[routing client], context.app.router]
(10) ["app", "apps", "options", "beforeHooks", "resolveHooks", "afterHooks", "matcher", "fallback", "mode", "history"]
Liste des clés [[routing client], context.app.router.options.routes]
(3) ["0", "1", "2"]
[routing client], context.app.router.options.routes= (3) [{}, {}, {}]0: {path: "/page1", name: "page1", component: ƒ}1: {path: "/page2", name: "page2", component: ƒ}2: {path: "/", name: "index", component: ƒ}length: 3__proto__: Array(0)
[index-routing], process.server= false process.client= true
[index-routing client], il y a 1 argument(s)
Liste des clés [[index-routing client], context]
(18) ["isStatic", "isDev", "isHMR", "app", "payload", "error", "base", "env", "redirect", "nuxtState", "route", "next", "_redirected", "_errored", "params", "query", "$axios", "from"]
Liste des clés [[index-routing client], context.app]
(14) ["router", "nuxt", "head", "render", "data", "beforeCreate", "created", "mounted", "watch", "computed", "methods", "components", "context", "$axios"]
Liste des clés [[index-routing client], context.route]
(8) ["name", "meta", "path", "hash", "query", "params", "fullPath", "matched"]
[index-routing client], context.route= {name: "index", meta: Array(1), path: "/", hash: "", query: {}, }
Liste des clés [[index-routing client], context.app.router]
(10) ["app", "apps", "options", "beforeHooks", "resolveHooks", "afterHooks", "matcher", "fallback", "mode", "history"]
Liste des clés [[index-routing client], context.app.router.options.routes]
(3) ["0", "1", "2"]
[index-routing client], context.app.router.options.routes= (3) [{}, {}, {}]0: {path: "/page1", name: "page1", component: ƒ}1: {path: "/page2", name: "page2", component: ƒ}2: {path: "/", name: "index", component: ƒ}length: 3__proto__: Array(0)
from= {name: "page1", meta: Array(1), path: "/page1", hash: "", query: {}, }
[home beforeCreate]
[home created]
[home beforeMount]
[home mounted]
  • lines 1-15: the client executes the [routing] middleware. This is normal. It is executed every time the route changes;
  • lines 16-29: the client executes the [index-routing] middleware, because:
    • [index] is the target of the current route (see line 23);
    • the page [index] has defined a middleware, namely [index-routing];

We can therefore see that general routing middleware is executed by the client before the middleware attached to the pages.