New Dec 27, 2024

Navigation guards in Nuxt 3 with defineNuxtRouteMiddleware

Company/Startup Blogs All from LogRocket Blog View Navigation guards in Nuxt 3 with defineNuxtRouteMiddleware on blog.logrocket.com

Navigation guards allow you to control who can access specific parts of your application, ensuring only authorized users reach certain pages. This enhances security and improves user experience by keeping your app safe and efficient.

Navigation Guards In Nuxt 3 With DefineNuxtRouteMiddleware

In this article, we’ll cover what navigation guards are, how they’re related to middleware, and how to write navigation guards in Nuxt 3. The term “middleware” will be used to refer to route middleware, which runs in the Vue part of your Nuxt apps and is a different beast from Nuxt’s server middleware.

What are navigation guards?

Navigation guards are pieces of code that run after a page is requested but before it’s rendered, to make sure only authorized users access certain functionality. Navigation guards are used to implement features like:

Creating navigation guards with Nuxt middleware

To create navigation guards, we’ll be taking advantage of a concept in Nuxt called middleware. Middleware in Nuxt are special functions that run before your page is rendered, making them ideal for implementing navigation guards.

There are three types of middleware in Nuxt: anonymous, named, and global. When you want to use anonymous and named middleware, you must explicitly define it on the page you want to guard. In contrast, global middleware applies to all pages.

Middleware syntax

This is the general form all middleware takes:

(from, to) => {
  if (someCondition) {
    return navigateTo("/route")
  }

if (otherCondition) { return abortNavigation() //or return abortNavigation(error) }

// return nothing to allow the request to proceed normally }

Note that middleware can also be async functions, which allow you to await asynchronous requests if you need to.

Every middleware is a function that takes two route objects as arguments:

Here are a couple of the route objects’ properties:

You can find a full list of properties here.

The functions navigateTo and abortNavigation are what allow you to implement the functionality of navigation guards. navigateTo lets you redirect a user to another path, and abortNavigation allows you to kill the navigation entirely. Both functions are globally available and thus don’t need to be imported.

Now that we know how to write a middleware function, let’s see how to register one, either for a specific page or globally.

Writing anonymous middleware

Anonymous middleware, also called inline middleware, is middleware that’s defined inside a page, and thus only ever runs on that page.

To write an anonymous middleware, all you have to do is write the middleware function inside the middleware array of the definePageMeta compiler macro.

Here’s an example:

// pages/staff.vue

<script setup> definePageMeta({ middleware: [ function (to, from) { // isUserAdmin() is a dummy function const isAdmin = isUserAdmin()

if (!isAdmin) { // Redirect the user to the homepage return navigateTo('/') }

// Else, proceed to the staff page } ] }) </script>

Note: you can define multiple anonymous middleware on one page, and they’ll be executed in the order they appear in the middleware array.

And that’s it! Next, we’ll look into writing named middleware.

Writing named middleware with defineNuxtRouteMiddleware

Named middleware is middleware that’s defined in a standalone file in the middleware directory, and can be run on multiple pages. The name of the middleware is the kebab-case name of its containing file, so the middleware that lives in the file middleware/isAdmin.js will get the name is-admin.

To create named middleware, we need one more thing: a helper function called defineNuxtRouteMiddleware. This helper function takes a middleware function as its only argument.

Putting it all together to create a named middleware, you first have to create a file in the middleware directory, then write your middleware, pass it to defineNuxtRouteMiddleware, and export the returned value. Here’s an example:

// middleware/isAdmin.js
export default defineNuxtRouteMiddleware((to, from) => {
      // isUserAdmin() is a dummy function
      const isAdmin = isUserAdmin()

if (!isAdmin) { // Redirect the user to the homepage return navigateTo('/') }

// Else, continue with navigation })

Now that you’ve created the is-admin middleware, you can use it on any page by putting its name in the middleware array of the definePageMeta macro. Here’s an example:

// pages/staff.vue

<script setup> definePageMeta({ middleware: ['is-admin'] }) </script>

And that’s it!

Writing global middleware

Writing global middleware works the same way as writing named middleware, with one difference: the name of the middleware file has to be suffixed with .global. So, the file will be named middleware/isAdmin.global.js instead of middleware/isAdmin.js, and then it’ll run on every route, without needing to be specified in definePageMeta.

Note that it’s not possible to define exceptions for global middleware — they always run on every route. If you want the global middleware to skip over a certain route, you have to write the check inside the middleware itself.

Middleware ordering

On any given route, middleware executes in this order:

Global middleware executes in alphabetical order based on the filename, but if you need to run middleware in a specific order, you can prefix it with a number, like so: middleware/01.isAdmin.global.js.

Best practices when working with navigation guards

The following are some guidelines to keep in mind when writing navigation guards.

You should write navigation guards to do as little work as possible; the longer each function takes to run, the longer the user has to wait for the page to be rendered.

You should avoid making network requests if possible, and if you can’t, you should fetch and cache as much data at once as you can.

Even though Nuxt guarantees the execution order of your middleware, each middleware function should be free of side effects and should run exactly the same regardless of where in the middleware chain it executes.

Be careful of infinite redirects

If you’re not careful, it’s easy to create infinite redirects by writing middleware like this:

export default defineNuxtRouteMiddleware((to, from) => {
    const { isAuthenticated } = dummyAuthFunction();

if (!isAuthenticated) { return navigateTo('/login') } else { return navigateTo('/') } })

This middleware looks fine at first glance, but it has a problem: you’re always redirecting, regardless of the value of the condition. This will create an infinite loop if this middleware will also be executed when you visit the pages you’re redirecting to. Here’s how you can modify the code to fix this:

export default defineNuxtRouteMiddleware((to, from) => {
    const { isAuthenticated } = dummyAuthFunction();

if (!isAuthenticated) { return navigateTo('/login') } })

The key here is, instead of redirecting to a specific route, return nothing (which allows the navigation to continue) when you don’t need to deflect the user.

Conclusion

Navigation guards are powerful features in Nuxt 3 that help you control access to your application’s routes. By leveraging middleware — whether anonymous, named, or global — you can efficiently implement these guards to enhance security and user experience. Keep your middleware lean and pure, and you’ll ensure smooth and effective navigation throughout your app.

The post Navigation guards in Nuxt 3 with defineNuxtRouteMiddleware appeared first on LogRocket Blog.

Scroll to top