Search code examples
next.jsnext.js13nextjs-dynamic-routingapp-routernextjs-rewrites

NextJS App Router: redirect all routes to two nested dynamic routes as root


was wondering if anyone could help with this issue I'm having learning the app router in regards to deeply nested dynamic routes.

Lets say I have an ecommerce shop which hosts multiple stores with their own available shopping modes (pickup and delivery) and would like to have the store id and shopping mode in the url: www.shop.com/store/STOREID/sm/SHOPPINGMODE.

When first time users visit www.shop.com they are first asked which shop they want and shopping mode (saved locally as a cookie for example) and any future visits it will redirect them from the root to the /store/STOREID/sm/SHOPPINGMODE url as mentioned.

The other thing is that if the user has a store selected and they decide to go www.shop.com/products it will redirect them to www.shop.com/store/STOREID/sm/SHOPPINGMODE/products instead.

So basically any url should have store/STOREID/sm/SHOPPINGMODE appended to it:

  • If first time visit: www.shop.com -> choose store -> www.shop.com/store/STOREID/sm/SHOPPINGMODE
  • If proceeding visit: www.shop.com -> www.shop.com/store/STOREID/sm/SHOPPINGMODE
  • If first time visit any url (such as products): www.shop.com/products -> choose store -> www.shop.com/store/STOREID/sm/SHOPPINGMODE/products
  • If proceeding visit any url (such as products): www.shop.com/products -> www.shop.com/store/STOREID/sm/SHOPPINGMODE/products

Hopefully that makes sense. What would be the most efficient way to implement this scenario? Middlewares? Redirects via config or in page? Something else?

Any help would be greatly appreciated!


Solution

  • I think using the scenario that you have mentioned, it's more efficient to use SHOPPINGMODE and STOREID in the same way to validate whether a user is logged in or not. Whoever your e-commerce needs to have this information, like app functionality, validating cookies in the middleware can guarantee it, like:

    // config matcher to not catch routes of static content or api and with store id and shopping mode
    
    export const config = {
      matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)',
      '/((?!(store\/[0-9]*\/sm\/(pickup|delivery))).*)'
      ],
    }
    export function middleware(request: NextRequest) {
    
      const HOME = '/'
    
        // First verify if this cookies exist
      
      const HAS_STORE_ID = request.cookies.has('X_STORE_ID')
      const HAS_SHOPPING_MODE =          request.cookies.has('X_SHOPPING_MODE')
      
      if(HAS_STORE_ID && HAS_SHOPPING_MODE) {
      
      // Get values
      
      const STORE_ID = request.cookies.get('X_STORE_ID')
      const SHOPPING_MODE = request.cookies.get('X_SHOPPING_MODE')
      
      // You can verify if are valid values on database here or redirect later catching the error of page not found
      
      verifyValidStoreCookies(STORE_ID,SHOPPING_MODE)
      
      const url = `/store/${STORE_ID}/sm/${SHOPPING_MODE}`
      
      return NextResponse.rewrite(new URL(url, request.url))
        const { pathname } = req.nextUrl
    
      }
      
      if (HAS_STORE_ID) {
        response.cookies.delete('X_STORE_ID')
    
        return NextResponse.rewrite(new URL(HOME, request.url))
    
      }
       if (HAS_SHOPPING_MODE) {
            response.cookies.delete('X_SHOPPING_MODE')
    
        return NextResponse.rewrite(new URL(HOME, request.url))
    
      }
      
        return NextResponse.rewrite(new URL(HOME, request.url))
    
    }

    You can use path search to append any paths to the end of the request URL and to verify if they are applicable or not, like:

    const prefixes = ['/shop', '/products']
    
    const { pathname } = request.nextUrl
     
      if (prefixes.some((prefix) => pathname.startsWith(prefix))) {
        const url = `/store/${STORE_ID}/sm/${SHOPPING_MODE}` + pathname
        return NextResponse.rewrite(new URL(url, request.url))
        ...
      }
     
      if (
        !pathname.endsWith('/') &&
        !pathname.match(/((?!\.products(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
      ) {
       ...
      }

    You can view more methods here: https://nextjs.org/docs/app/building-your-application/routing/middleware

    But it is always only one approach; it can change and be improved depending on your business rules. I hope it helps in some way.