Access Control
Limit the access of content.
Overview
Thanks to the flexible design, it is simple to implement access control with Fumadocs.
Loader API
If you are using loader() API, you can create the loaders on-demand, and filter files from content source to restrict accessible content.
For example, this setup with Fumadocs MDX only shows the MDX files with frontmatter permission: public.
import { docs } from 'collections/server';
import { loader, update } from 'fumadocs-core/source';
const filteredSource = update(docs.toFumadocsSource())
.files((files) =>
files.filter((file) => {
// keep meta files (e.g. `meta.json`)
if (file.type === 'meta') return true;
// access file data (type-safe)
return file.data.permission === 'public';
}),
)
.build();
export const source = loader(filteredSource, {
baseUrl: '/docs',
});For more complicated setups, you can turn source into a factory function.
import { docs } from 'collections/server';
import { loader, update } from 'fumadocs-core/source';
// uncached, it's better to cache it in-memory for real use case.
export function createSource(permission: 'public' | 'admin') {
const filteredSource = update(docs.toFumadocsSource())
.files((files) =>
files.filter((file) => {
if (file.type === 'meta') return true;
return file.data.permission === permission;
}),
)
.build();
return loader(filteredSource, {
baseUrl: '/docs',
});
}Advantages
What is special with this approach is that the access is limited at input level, rather than at rendering phase or build-time.
This guarantees the protected content is filtered entirely, and without compromise on flexibility.
Custom Implementation
Without the Loader API, implement access control in Waku, such as in routes or middleware.
For example, to limit access to certain pages:
import type { PageProps } from 'waku/router';
import { unstable_notFound } from 'waku/router/server';
import { source } from '@/lib/source';
export default async function Page({ slugs }: PageProps<'/docs/[...slugs]'>) {
const user = await getUser();
const page = source.getPage(slugs);
if (!page) unstable_notFound();
if (page.data.permission !== user.permission) unstable_notFound();
// render page...
}
// render per request, static pages are built without a user
export const getConfig = async () => ({ render: 'dynamic' }) as const;Note that it is up to you to manage the page tree (sidebar items), search, Markdown routes and other details.
This approach gives the most flexibility, but Fumadocs does not handle the access control. The setup takes more work, so consult the Waku docs when implementing it.
How is this guide?
Last updated on
