T
TanStack10mo ago
like-gold

Property way to handle not found components with permissions

Hello, we are building an dashboard where i already handle authentication, but a user also has permissions to view certain pages or not. This i how i thought i would handle this
export const Route = createFileRoute("/_app/claims/$claimId")({
context: () => ({
required_permission: "can_view_claim",
}),
beforeLoad: async ({
context: { queryClient, required_permission },
}): Promise<void> => {
const user = await queryClient.ensureQueryData(authQueries.getMe())
if (!user.checkPermission(required_permission))
throw notFound({ data: "no-permission" })
},
loader: async ({ params: { claimId }, context: { queryClient } }) => {
const data = await queryClient.ensureQueryData(
claimsQueries.getById({ claimId })
)
if (!data) throw notFound({ data: "not-found" })
},

component: () => ClaimLayout(),
notFoundComponent: (props) => {
return <p>{props.data}</p>
},
})
export const Route = createFileRoute("/_app/claims/$claimId")({
context: () => ({
required_permission: "can_view_claim",
}),
beforeLoad: async ({
context: { queryClient, required_permission },
}): Promise<void> => {
const user = await queryClient.ensureQueryData(authQueries.getMe())
if (!user.checkPermission(required_permission))
throw notFound({ data: "no-permission" })
},
loader: async ({ params: { claimId }, context: { queryClient } }) => {
const data = await queryClient.ensureQueryData(
claimsQueries.getById({ claimId })
)
if (!data) throw notFound({ data: "not-found" })
},

component: () => ClaimLayout(),
notFoundComponent: (props) => {
return <p>{props.data}</p>
},
})
There is only a slight differnce when i throw these. When i throw inside the loader the notFoundComponent of this route is rendered, when i throw the notFound function inside the beforeLoad the root notFound component is rendered. Should i just do both checks in the loader functions, am i missing somthing or should i do a complete different approach?
2 Replies
flat-fuchsia
flat-fuchsia10mo ago
yes notFound behaves differently for beforeLoad and loader seems like throwing only in loader would work for you
like-gold
like-goldOP10mo ago
Thanks! indeed im now just doing this inside the loader function, Would be really nice if we could have something like this though
const checkAuth = createMiddleware({
params: { claimId },
context: { queryClient, required_permission },
}) => {
try {
await queryClient.ensureQueryData(authQueries.getMe())
} catch {
throw redirect({
to: "/login",
search: {
redirect: location.href,
},
})
}
}

const checkPermission = createMiddleware({
params: { claimId },
context: { queryClient, required_permission },
}) => {
const user = await queryClient.ensureQueryData(authQueries.getMe())
if (!user.checkPermission(required_permission))
throw notFound({ data: "no-permission" })
}

export const Route = createFileRoute("/_app/claims/$claimId")({
context: ({ params: { claimId }, context: { queryClient } }) => ({
required_permission: "can_view_claim",
}),
middleware: [checkAuth, checkPermission]
const checkAuth = createMiddleware({
params: { claimId },
context: { queryClient, required_permission },
}) => {
try {
await queryClient.ensureQueryData(authQueries.getMe())
} catch {
throw redirect({
to: "/login",
search: {
redirect: location.href,
},
})
}
}

const checkPermission = createMiddleware({
params: { claimId },
context: { queryClient, required_permission },
}) => {
const user = await queryClient.ensureQueryData(authQueries.getMe())
if (!user.checkPermission(required_permission))
throw notFound({ data: "no-permission" })
}

export const Route = createFileRoute("/_app/claims/$claimId")({
context: ({ params: { claimId }, context: { queryClient } }) => ({
required_permission: "can_view_claim",
}),
middleware: [checkAuth, checkPermission]
Just like the middleware Tanstack/Start is getting so we can just reuse these functions across routes

Did you find this page helpful?