How to make a middleware Check Permission to access some page

How to make a middleware like Check permission to access some page, so we can send what the roles that can do some action. Something like this I think.
definePageMeta({
middleware: ['checkPermission:1']
})
definePageMeta({
middleware: ['checkPermission:1']
})
24 Replies
kapa.ai
kapa.ai2w ago
Hi @Adnan Erlansyah. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To create a middleware for checking permissions to access certain pages in Nuxt, you can follow these steps: 1. Create a named middleware file in the middleware/ directory:
// middleware/check-permission.ts
export default defineNuxtRouteMiddleware((to) => {
const userStore = useUserStore(); // Assuming you have a user store with roles
const requiredRole = to.meta.requiredRole;

if (!userStore.hasRole(requiredRole)) {
// Redirect to an unauthorized page or show an error
return navigateTo('/unauthorized');
}
})
// middleware/check-permission.ts
export default defineNuxtRouteMiddleware((to) => {
const userStore = useUserStore(); // Assuming you have a user store with roles
const requiredRole = to.meta.requiredRole;

if (!userStore.hasRole(requiredRole)) {
// Redirect to an unauthorized page or show an error
return navigateTo('/unauthorized');
}
})
2. Use the middleware in your page component:
<script setup>
definePageMeta({
middleware: ['check-permission'],
requiredRole: 1 // Set the required role for this page
})
</script>
<script setup>
definePageMeta({
middleware: ['check-permission'],
requiredRole: 1 // Set the required role for this page
})
</script>
This approach is based on the information from Nuxt Documentation on Route Middleware. However, the exact syntax you proposed (middleware: ['checkPermission:1']) is not directly supported in Nuxt 3. To achieve a similar functionality, you would need to: 1. Pass the required role as metadata in definePageMeta. 2. Access this metadata in your middleware function. 3. Check the user's role against the required role. Remember that the implementation details may vary depending on how you manage user roles and authentication in your application. You might need to adjust the code to fit your specific user management system. Nuxt Documentation on definePageMeta provides more information on how to use custom metadata in your pages, which can be accessed in your middleware functions. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1079709612078534676> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Adnan Erlansyah
Adnan ErlansyahOP2w ago
@kapa.ai how about to make abort unauthorized access?
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- To abort unauthorized access in a Nuxt middleware, you can use the abortNavigation() helper function. This function prevents the navigation from taking place and can be used to handle unauthorized access attempts. Here's how you can modify the middleware to abort unauthorized access:
// middleware/check-permission.ts
export default defineNuxtRouteMiddleware((to) => {
const userStore = useUserStore(); // Assuming you have a user store with roles
const requiredRole = to.meta.requiredRole;

if (!userStore.hasRole(requiredRole)) {
// Abort the navigation with an error message
return abortNavigation('Unauthorized access. You do not have the required permissions.');
}
})
// middleware/check-permission.ts
export default defineNuxtRouteMiddleware((to) => {
const userStore = useUserStore(); // Assuming you have a user store with roles
const requiredRole = to.meta.requiredRole;

if (!userStore.hasRole(requiredRole)) {
// Abort the navigation with an error message
return abortNavigation('Unauthorized access. You do not have the required permissions.');
}
})
In this example, if the user doesn't have the required role, the abortNavigation() function is called with an error message. This will prevent the navigation to the protected page and throw an error. You can use this middleware in your page component as shown before:
<script setup>
definePageMeta({
middleware: ['check-permission'],
requiredRole: 1 // Set the required role for this page
})
</script>
<script setup>
definePageMeta({
middleware: ['check-permission'],
requiredRole: 1 // Set the required role for this page
})
</script>
The abortNavigation() function is a built-in Nuxt utility that's specifically designed for use in route middleware. It's important to note that: 1. abortNavigation() is only usable inside a route middleware handler. 2. You can optionally pass an error message or an Error object to abortNavigation(). For more details on abortNavigation() and its usage, you can refer to the Nuxt documentation on abortNavigation. Remember to handle the aborted navigation appropriately in your application, possibly by showing an error message to the user or redirecting them to an error page. Nuxt Documentation on Middleware provides more information on how to use and customize route middleware in your Nuxt application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1079709612078534676> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Adnan Erlansyah
Adnan ErlansyahOP2w ago
@kapa.ai I make a custom error page, but why the message is not same as the message from method abortNavigation. this the component of error page.
<template>
<div>
<h2>{{ error?.statusCode }}</h2>
<p>{{ errorMessage }}</p>
<button type="button" @click="handleError">Kembali ke halaman awal</button>
</div>
</template>

<script setup lang="ts">
import type { NuxtError } from '#app'

const props = defineProps({
error: Object as () => NuxtError
})

const titleErrorMessage = computed(() => {
switch (props.error!.statusCode) {
case 404:
return "Page Not Found"
case 500:
return "Server Error"
default:
return "An Error Occurred"
}
})

// Title & Meta Tags
useHead({
title: `${props.error!.statusCode} - ${titleErrorMessage.value}`,
})

const errorMessage = computed(() => {
switch (props.error?.statusCode) {
case 404:
return "Whoops! The page you're looking for doesn't exist"
case 500:
return "Oops! Something went wrong on our server"
default:
return props.error?.message
}
})

const handleError = () => clearError({ redirect: "/" })

</script>

<style scoped>

</style>
<template>
<div>
<h2>{{ error?.statusCode }}</h2>
<p>{{ errorMessage }}</p>
<button type="button" @click="handleError">Kembali ke halaman awal</button>
</div>
</template>

<script setup lang="ts">
import type { NuxtError } from '#app'

const props = defineProps({
error: Object as () => NuxtError
})

const titleErrorMessage = computed(() => {
switch (props.error!.statusCode) {
case 404:
return "Page Not Found"
case 500:
return "Server Error"
default:
return "An Error Occurred"
}
})

// Title & Meta Tags
useHead({
title: `${props.error!.statusCode} - ${titleErrorMessage.value}`,
})

const errorMessage = computed(() => {
switch (props.error?.statusCode) {
case 404:
return "Whoops! The page you're looking for doesn't exist"
case 500:
return "Oops! Something went wrong on our server"
default:
return props.error?.message
}
})

const handleError = () => clearError({ redirect: "/" })

</script>

<style scoped>

</style>
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- The issue you're experiencing with the error message not matching the one provided in abortNavigation() is related to how Nuxt handles errors and the structure of the error object. When you use abortNavigation() in a middleware, it creates an error, but the message you pass to it doesn't directly become the message property of the error object that's passed to your error page. Instead, it's typically set as the statusMessage. To access the message you set in abortNavigation(), you need to modify your error page component to use the statusMessage property. Here's how you can update your code:
<template>
<div>
<h2>{{ error?.statusCode }}</h2>
<p>{{ errorMessage }}</p>
<button type="button" @click="handleError">Kembali ke halaman awal</button>
</div>
</template>

<script setup lang="ts">
import type { NuxtError } from '#app'

const props = defineProps({
error: Object as () => NuxtError
})

const titleErrorMessage = computed(() => {
switch (props.error!.statusCode) {
case 404:
return "Page Not Found"
case 500:
return "Server Error"
default:
return "An Error Occurred"
}
})

useHead({
title: `${props.error!.statusCode} - ${titleErrorMessage.value}`,
})

const errorMessage = computed(() => {
if (props.error?.statusMessage) {
return props.error.statusMessage
}
switch (props.error?.statusCode) {
case 404:
return "Whoops! The page you're looking for doesn't exist"
case 500:
return "Oops! Something went wrong on our server"
default:
return props.error?.message || "An unknown error occurred"
}
})

const handleError = () => clearError({ redirect: "/" })
</script>
<template>
<div>
<h2>{{ error?.statusCode }}</h2>
<p>{{ errorMessage }}</p>
<button type="button" @click="handleError">Kembali ke halaman awal</button>
</div>
</template>

<script setup lang="ts">
import type { NuxtError } from '#app'

const props = defineProps({
error: Object as () => NuxtError
})

const titleErrorMessage = computed(() => {
switch (props.error!.statusCode) {
case 404:
return "Page Not Found"
case 500:
return "Server Error"
default:
return "An Error Occurred"
}
})

useHead({
title: `${props.error!.statusCode} - ${titleErrorMessage.value}`,
})

const errorMessage = computed(() => {
if (props.error?.statusMessage) {
return props.error.statusMessage
}
switch (props.error?.statusCode) {
case 404:
return "Whoops! The page you're looking for doesn't exist"
case 500:
return "Oops! Something went wrong on our server"
default:
return props.error?.message || "An unknown error occurred"
}
})

const handleError = () => clearError({ redirect: "/" })
</script>
In this updated version, we first check for props.error?.statusMessage in the errorMessage computed property. If it exists, we return it. This will display the message you set in abortNavigation(). If statusMessage is not present, it falls back to the switch statement you originally had. This approach allows you to see the custom message from abortNavigation() while still maintaining your default error messages for other scenarios. For more information on error handling in Nuxt, you can refer to the Nuxt Documentation on Error Handling. Remember that the exact behavior might vary depending on your Nuxt version and configuration. Always refer to the most up-to-date documentation for your specific Nuxt version. Link title ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1079709612078534676> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Adnan Erlansyah
Adnan ErlansyahOP2w ago
@kapa.ai how about to make a middleware that only run once when it's first initial of app.
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- To create a middleware that only runs once during the initial load of your Nuxt application, you can use the callOnce utility function. This function is designed to execute a given function or block of code only once during server-side rendering (but not hydration) and client-side navigation. Here's how you can implement this: 1. Create a new middleware file, for example, middleware/init.global.ts:
import { callOnce } from '#app'

export default defineNuxtRouteMiddleware(() => {
callOnce(async () => {
console.log('This middleware will only run once')
// Your initialization logic here
// For example, fetching initial app configuration
// const config = await $fetch('<https://api.example.com/app-config'>)
// useState('appConfig', () => config)
})
})
import { callOnce } from '#app'

export default defineNuxtRouteMiddleware(() => {
callOnce(async () => {
console.log('This middleware will only run once')
// Your initialization logic here
// For example, fetching initial app configuration
// const config = await $fetch('<https://api.example.com/app-config'>)
// useState('appConfig', () => config)
})
})
2. The middleware will automatically be applied globally due to the .global.ts suffix in the filename. This approach ensures that your initialization code runs only once, either during server-side rendering or on the first client-side navigation, whichever happens first. Key points to note: - The callOnce function is available since Nuxt v3.9 as mentioned in the Nuxt documentation. - This method is particularly useful for setting up global state, logging events, or performing heavy computations that should only happen once. - The code inside callOnce should not have external dependencies or refer to variables defined outside the callback. - If you need to do data fetching and want it to be reactive, you should use useAsyncData or useFetch instead. Remember that callOnce is a composable meant to be called directly in a setup function, plugin, or route middleware, as it needs to add data to the Nuxt payload to avoid re-calling the function on the client when the page hydrates. For more detailed information and examples, you can refer to the Nuxt documentation on callOnce. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1079709612078534676> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Cake
Cake2w ago
a plugin
Adnan Erlansyah
Adnan ErlansyahOP2w ago
What do u mean by plugin sir? I think it's just used method callOnce right?
Cake
Cake2w ago
a nuxt plugin
Adnan Erlansyah
Adnan ErlansyahOP2w ago
as kapa said
Cake
Cake2w ago
not a nuxt middelware look up nuxt plugins those will run once
Adnan Erlansyah
Adnan ErlansyahOP2w ago
Could you give me a link to those plugins?
Cake
Cake2w ago
Nuxt
plugins/ · Nuxt Directory Structure
Nuxt has a plugins system to use Vue plugins and more at the creation of your Vue application.
Adnan Erlansyah
Adnan ErlansyahOP2w ago
Oh so it's like we use some hooks to direct the action immediately when the app is first initialized?
Cake
Cake2w ago
depends on what you're trying to do and what you mean hooks
Adnan Erlansyah
Adnan ErlansyahOP2w ago
I mean the lifecycle as you can see from this hooks.
No description
Adnan Erlansyah
Adnan ErlansyahOP2w ago
@kapa.ai I got thiss issue when I used method callOnce and there's a store inside it.
ERROR API Health Check Failed: [nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a
Nuxt bug. Find out more at https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables.
ERROR API Health Check Failed: [nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a
Nuxt bug. Find out more at https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables.
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- The error you're encountering is a common issue when using composables or Nuxt-specific functions outside of their intended context. In this case, it seems that the store you're trying to access within the callOnce function is causing the problem. The callOnce utility is designed to run code only once, but it doesn't automatically provide the correct Nuxt context for all operations. When you try to access a store or other Nuxt composables inside callOnce, it can lead to this error. To resolve this issue, you have a few options: 1. Use runWithContext: You can wrap your store access inside runWithContext to ensure it has the correct Nuxt context:
import { callOnce, useNuxtApp } from '#app'

export default defineNuxtRouteMiddleware(() => {
const nuxtApp = useNuxtApp()

callOnce(() => {
nuxtApp.runWithContext(() => {
// Your store operations here
const store = useYourStore()
// ... other code
})
})
})
import { callOnce, useNuxtApp } from '#app'

export default defineNuxtRouteMiddleware(() => {
const nuxtApp = useNuxtApp()

callOnce(() => {
nuxtApp.runWithContext(() => {
// Your store operations here
const store = useYourStore()
// ... other code
})
})
})
This approach is described in the Nuxt documentation on runWithContext. 2. Move store operations outside callOnce: If possible, you can restructure your code to perform store operations outside of callOnce:
export default defineNuxtRouteMiddleware(() => {
const store = useYourStore()

callOnce(() => {
// Use store.value or store methods here
console.log('This will only run once')
// Other non-store related operations
})
})
export default defineNuxtRouteMiddleware(() => {
const store = useYourStore()

callOnce(() => {
// Use store.value or store methods here
console.log('This will only run once')
// Other non-store related operations
})
})
3. Use useState for global state: If you're using the store for global state, consider using useState instead, which is designed to work well with SSR and hydration:
export default defineNuxtRouteMiddleware(() => {
const globalState = useState('globalState', () => ({}))

callOnce(() => {
// Modify globalState here
globalState.value.someProperty = 'someValue'
})
})
export default defineNuxtRouteMiddleware(() => {
const globalState = useState('globalState', () => ({}))

callOnce(() => {
// Modify globalState here
globalState.value.someProperty = 'someValue'
})
})
Remember that callOnce is primarily designed for operations that don't require reactivity or don't depend on the Nuxt context. For more complex scenarios involving stores or other Nuxt-specific features, you might need to reconsider your approach or use other Nuxt lifecycle hooks. For more information on this error and composable usage, you can refer to the Nuxt documentation on Auto-imports. If these solutions don't resolve your specific issue, you might need to provide more details about your store setup and how you're using it within the middleware. Nuxt Documentation on Auto-imports Nuxt Documentation on runWithContext ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1079709612078534676> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?