@tradecrush/next-route-guard
Version:
Convention-based route authentication middleware for Next.js applications
212 lines (205 loc) • 8.12 kB
text/typescript
import * as next_server from 'next/server';
import { NextRequest, NextResponse, NextFetchEvent } from 'next/server';
/**
* Type definitions for Next Route Guard
*
* This module defines the TypeScript interfaces and types used throughout the package.
*/
/**
* Type alias for a Next.js middleware function
* This is the standard signature for Next.js middleware
*/
type NextMiddleware = (request: NextRequest) => Promise<NextResponse | undefined> | NextResponse | undefined;
/**
* Route map for protected and public routes
*
* This interface represents the structure of the JSON file generated during build time
* that contains the lists of protected and public routes based on directory conventions.
*/
interface RouteMap {
/**
* Array of paths that are protected and require authentication
* These paths will trigger authentication checks when accessed
*/
protected: string[];
/**
* Array of paths that are public and don't require authentication
* These paths are freely accessible without authentication
*/
public: string[];
}
/**
* Configuration options for creating a route guard middleware
*
* These options control how the middleware behaves, including how to check
* authentication, how to handle unauthenticated requests, and which routes
* to protect or exclude.
*/
interface RouteGuardOptions {
/**
* Function to determine if a user is authenticated
*
* This function is called for protected routes to check if the user is authenticated.
* You should implement this function based on your authentication system (JWT, cookies, etc.)
*
* @example
* isAuthenticated: (request) => {
* const token = request.cookies.get('auth-token')?.value;
* return !!token;
* }
*/
isAuthenticated: (request: NextRequest) => Promise<boolean> | boolean;
/**
* Function to handle unauthenticated requests
*
* This function is called when a user tries to access a protected route
* without being authenticated. The default behavior redirects to /login.
*
* @default Redirects to /login with the original URL as a 'from' parameter
*
* @example
* onUnauthenticated: (request) => {
* if (request.nextUrl.pathname.startsWith('/api/')) {
* return new NextResponse(
* JSON.stringify({ error: 'Authentication required' }),
* { status: 401, headers: { 'Content-Type': 'application/json' } }
* );
* }
*
* // Default login redirect for non-API routes
* const url = request.nextUrl.clone();
* url.pathname = '/login';
* url.searchParams.set('from', request.nextUrl.pathname);
* return NextResponse.redirect(url);
* }
*/
onUnauthenticated?: (request: NextRequest) => Promise<NextResponse> | NextResponse;
/**
* Map of protected and public routes
*
* This is the route map generated during build time by the CLI tools.
* It contains the lists of protected and public routes based on directory conventions.
*
* This must be generated at build time to work with Edge runtime since it can't
* scan the filesystem during execution.
*
* @example
* // Import the generated route map
* import routeMap from './app/route-map.json';
*/
routeMap: RouteMap;
/**
* Default behavior for routes not explicitly marked in the route map
*
* When set to true (default), routes are protected unless explicitly marked as public.
* When set to false, routes are public unless explicitly marked as protected.
*
* @default true - Routes are protected by default
*/
defaultProtected?: boolean;
/**
* URLs to exclude from authentication checks
*
* These URLs will bypass the authentication check entirely, regardless of
* whether they are in the protected routes list.
*
* Can be strings with glob-style wildcards or RegExp objects.
*
* @default ['/api/(.*)'] - Excludes all API routes
*
* @example
* excludeUrls: [
* '/api/(.*)', // All API routes
* '/static/(.*)', // Static assets
* '/public/(.*)' // Anything under /public/
* ]
*/
excludeUrls?: (string | RegExp)[];
}
/**
* Core implementation of Next Route Guard middleware.
* This module contains the runtime logic for checking if a route should be protected
* and enforcing authentication based on the route map generated at build time.
*/
/**
* Creates a Next.js middleware function that enforces route authentication
* based on the directory structure conventions in the app router.
*
* This is the main entry point for the runtime middleware that checks if a user
* is authenticated and handles redirection for protected routes.
*
* @param options - Configuration options for the middleware
* @returns A Next.js middleware function
*/
declare function createRouteGuardMiddleware(options: RouteGuardOptions): (request: NextRequest) => Promise<NextResponse<unknown>>;
/**
* A middleware function that takes a request and returns a response.
* This is compatible with Next.js middleware system.
*/
type Middleware = (request: next_server.NextRequest, event?: NextFetchEvent) => Promise<next_server.NextResponse | undefined> | next_server.NextResponse | undefined;
/**
* A middleware factory that wraps a middleware.
* This is used to create reusable middleware components that can be chained together.
*/
type MiddlewareFactory = (middleware: Middleware) => Middleware;
/**
* Chain multiple middleware factories together.
*
* This allows you to compose multiple middleware functions into a single middleware pipeline.
* Each middleware in the chain can choose to call the next middleware or short-circuit the chain.
*
* @param functions - Array of middleware factories to chain together
* @param index - Current index in the chain (used internally for recursion)
* @returns A middleware function representing the entire chain
*
* @example
* ```ts
* // Create middleware factories
* const withLogging: MiddlewareFactory = (next) => {
* return (request) => {
* console.log(`Request: ${request.method} ${request.url}`);
* return next(request);
* };
* };
*
* const withAuth: MiddlewareFactory = (next) => {
* return (request) => {
* if (!isAuthenticated(request)) {
* return NextResponse.redirect('/login');
* }
* return next(request);
* };
* };
*
* // Use the chain
* export default function middleware(req: NextRequest, ev: NextFetchEvent) {
* return chain([withLogging, withAuth])(req, ev);
* }
* ```
*/
declare function chain(functions: MiddlewareFactory[], index?: number): Middleware;
/**
* Generate a route map based on the Next.js app directory structure.
*
* This function analyzes the directory structure to identify routes and their protection status.
* It's used by the CLI tools to generate the route map at build time or during development.
*
* Routes are classified as protected or public based on their directory context:
* - Routes inside a "(public)" directory group are marked as public
* - Routes inside a "(protected)" directory group are marked as protected
* - Routes inherit protection status from their parent directories
* - Routes are protected by default if not explicitly marked
*
* @param appDir - Path to the Next.js app directory
* @param publicPatterns - Array of directory name patterns that indicate public routes
* @param protectedPatterns - Array of directory name patterns that indicate protected routes
* @returns Object containing either the generated route map or an error message
*/
declare function generateRouteMap(appDir: string, publicPatterns?: string[], protectedPatterns?: string[]): {
error?: string;
routeMap?: {
public: string[];
protected: string[];
};
};
export { type Middleware, type MiddlewareFactory, type NextMiddleware, type RouteGuardOptions, type RouteMap, chain, createRouteGuardMiddleware, generateRouteMap };