npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

guarded-routes-next

v1.0.2

Published

Simple Guarded routes management for Next.js

Downloads

707

Readme

guarded-routes-next

guarded-routes-next is a package for managing route guards in Next.js applications. It was born out of identifying there is a code duplication between checking if a tab exists and blocking/allowing its relevant route.

It allows you to define role-based, feature-flagged, special-check routes, or any other check you can think of and provides middleware to enforce these guards.

It's intended to be used as an infrastructure that enables you to make the same checks in your middleware and your tabs seamlessly

After configuring, you get 2 artifacts

  • isPathAllowed function that is used both in the tabs and in the middle ware,
  • the middleware itself - guardMiddleware

This infra is scalable and flexible and is production ready. Keep in mind that limitations of Next.js still apply (for example, the middleware in Next.js run in an Edge runtime context)

Installation

To install the package, run:

yarn add guarded-routes-next

or

npm install guarded-routes-next

Usage

Define Guarded Routes

Create a types.ts file to define the structure of your guarded routes:

// src/guarded-routes/types.ts
export interface GuardedRoutes {
  featureFlaggedRoutes: Record<string, string[]>;
  roleBasedRoutes: Record<string, any[]>;
  specialChecksRoutes?: Record<string, () => Promise<boolean>>;
  defaultRedirects?: Record<
    string,
    (pathname: string, searchParams: URLSearchParams) => Promise<string>
  >;
}

Implement Middleware and checks

Create a guardMiddleware.ts file to implement the middleware logic:

// src/guarded-routes/guardMiddleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { getFlags } from '@/app/server-actions';
import { generateGuardedRoutes, GetApplicationGuardedRoutesFunc, searchMapForRoute } from 'guarded-routes-next';
import { GuardedRoutes } from '@/guarded-routes/types';
import { safePageGuardedRoutes } from '@/app/safe-page/guardedRoutes';

// this is an example of using a function to check for roles
const checkRouteForRole = (getApplicationGuardedRoutes: GetApplicationGuardedRoutesFunc<GuardedRoutes>) => async (pathname: string) => {
  const { roleBasedRoutes } = getApplicationGuardedRoutes();
  const roles: string[] | undefined = searchMapForRoute(pathname, roleBasedRoutes);
  if (!roles) return true;
  return !roles.includes('admin');
};

// this is an example of using a function to check for feature flags
const checkRouteForFeatureFlag = (getApplicationGuardedRoutes: GetApplicationGuardedRoutesFunc<GuardedRoutes>) => async (pathname: string) => {
  const { featureFlaggedRoutes } = getApplicationGuardedRoutes();
  const featureFlags: string[] | undefined = searchMapForRoute(pathname, featureFlaggedRoutes);
  if (!featureFlags) return true;
  return await getFlags(featureFlags);
};

// this is an example of using a function to check for custom special conditions
const checkRouteForSpecialChecks = (getApplicationGuardedRoutes: GetApplicationGuardedRoutesFunc<GuardedRoutes>) => async (pathname: string) => {
  const { specialChecksRoutes } = getApplicationGuardedRoutes();
  const relevantRouteValues = searchMapForRoute(pathname, specialChecksRoutes!);
  if (!relevantRouteValues) return true;
  return relevantRouteValues();
};

// here you define how the middleware should  behave when a route is rejected (isPathAllowed returns false)
const middlewareReject = (getApplicationGuardedRoutes: GetApplicationGuardedRoutesFunc<GuardedRoutes>) => async (request: NextRequest) => {
  const { nextUrl } = request;
  const { host, protocol, pathname, searchParams } = nextUrl;
  const defaultRedirect = searchMapForRoute(pathname, getApplicationGuardedRoutes().defaultRedirects!);
  const targetRedirect = defaultRedirect && (await defaultRedirect(pathname, searchParams));
  return targetRedirect
    ? NextResponse.redirect(`${protocol}${host}${targetRedirect}`)
    : NextResponse.error('not found', { status: 404 });
};

// this function gets the application guarded routes from the generateGuardedRoutes function
// it will result in the isPathAllowed function being able to check the route against the guarded routes
const isPathAllowedWithGetApplicationGuardedRoutes = (getApplicationGuardedRoutes: GetApplicationGuardedRoutesFunc<GuardedRoutes>) => async (pathname: string) => {
  return (
    (await checkRouteForRole(getApplicationGuardedRoutes)(pathname)) &&
    (await checkRouteForFeatureFlag(getApplicationGuardedRoutes)(pathname)) &&
    (await checkRouteForSpecialChecks(getApplicationGuardedRoutes)(pathname))
  );
};

const { isPathAllowed, guardMiddleware } = generateGuardedRoutes({
  isPathAllowedWithGetApplicationGuardedRoutes,
  initValue: {
    roleBasedRoutes: {},
    specialChecksRoutes: {},
    featureFlaggedRoutes: {}
  } as GuardedRoutes,
  applicationGuardedRoutes: [safePageGuardedRoutes],
  middlewareReject
});

export { guardMiddleware, isPathAllowed };

Configure Middleware in Next.js

In your middleware.ts file, configure the middleware:

// middleware.ts
import { guardMiddleware } from '@/guarded-routes/guardMiddleware';

export function middleware(request: NextRequest) {
  return guardMiddleware(request); // you can add other middlewares here
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
};

Example Usage

Define your guarded routes in a separate file, one for each layout or feature:

// src/app/safe-page/guardedRoutes.ts
import { GuardedRoutes } from '@/guarded-routes/types';

export const safePageGuardedRoutes: GuardedRoutes = {
  roleBasedRoutes: {
    '/admin-only-route': ['admin']
  },
  featureFlaggedRoutes: {
    '/beta-route': ['betaFeature']
  },
  specialChecksRoutes: {
    '/special-route': async () => {
      // Custom check logic
      return true;
    }
  },
  defaultRedirects: { //this is an example of how to redirect to a default route in case of rejection
    '/beta-route': async (pathname, searchParams) => {
      // Custom redirect logic
      return '/login';
    }
  }
};

Finally, you can create a simple Tab RSC and use it as a wrapper for your client componentTab:

// src/component/tabs/Tab.tsx
export async function Tab(props:TabProps) {
    if (!await isPathAllowed(props.path)) {
        return null;
    }
    return <ClientTab {...props} />;
}

This way you don't need any checks inline in your JSX and your tabs are "clean".

License

This project is licensed under the MIT License.