Search code examples
amazon-web-servicesnext.jsamazon-cloudfrontdynamic-routing

NextJS dynamic routing in Amazon CloudFront


I have an application that uses NextJS as a wrapper, and I make use of NextJS's dynamic routing feature. I had a problem when deploying it to CloudFront due to dns.com/path/page not being rendered, instead CloudFront expected it to be dns.com/path/page.html. I worked it around by applying this lambda-edge-nice-url solution. It works properly now. However, there's still one issue left: NextJS's dynamic routes. dsn.com/path/subpath/123 should work, since 123 is a dynamic parameter. However, that does no work. In only returns the page when I access dns.com/path/subpath/[id], which of course is not correct, since [id] is not a parameter I want to load.

The strangest thing is: if I try to access the URL as I stated above directly, it fails. However, inside the application I have buttons and links that redirect the user, and that works properly.

Navigating from inside the application (button with router.push inside its callback): enter image description here

Trying to access the url directly: enter image description here

Can anyone help me to properly route the requests?


Solution

  • After trying a lot of different code, I finally came up with a Lambda edge expression that fixed two issues in one:

    • The need to insert .html at the end of the URL
    • NextJS dynamic routes that were't working on refresh or when accessed directly to URL.

    The code below basically takes care of dynamic routes first. It uses a regex expression to understand the current URL and redirect the request to the proper [id].html file. After that, if the none of the regex are matched, and if the URL does not contain .html extension, it adds the extension and retrieves the correct file.

    const config = {
        suffix: '.html',
        appendToDirs: 'index.html',
        removeTrailingSlash: false,
    };
    
    const regexSuffixless = /\/[^/.]+$/; // e.g. "/some/page" but not "/", "/some/" or "/some.jpg"
    const regexTrailingSlash = /.+\/$/; // e.g. "/some/" or "/some/page/" but not root "/"
    const dynamicRouteRegex = /\/subpath\/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/; // e.g /urs/some-uuid; // e.g. '/subpath/uuid'
    
    exports.handler = function handler(event, context, callback) {
        const { request } = event.Records[0].cf;
        const { uri } = request;
        const { suffix, appendToDirs, removeTrailingSlash } = config;
        
        //Checks for dynamic route and retrieves the proper [id].html file
        if (uri.match(dynamicRouteRegex)) {
            request.uri = "/subpath/[id].html";
            callback(null, request);
            return;
        }
        
        
        // Append ".html" to origin request
        if (suffix && uri.match(regexSuffixless)) {
            request.uri = uri + suffix;
            callback(null, request);
            return;
        }
        
        // Append "index.html" to origin request
        if (appendToDirs && uri.match(regexTrailingSlash)) {
            request.uri = uri + appendToDirs;
            callback(null, request);
            return;
        }
    
        // Redirect (301) non-root requests ending in "/" to URI without trailing slash
        if (removeTrailingSlash && uri.match(/.+\/$/)) {
            const response = {
                // body: '',
                // bodyEncoding: 'text',
                headers: {
                    'location': [{
                        key: 'Location',
                        value: uri.slice(0, -1)
                     }]
                },
                status: '301',
                statusDescription: 'Moved Permanently'
            };
            callback(null, response);
            return;
        }
    
        // If nothing matches, return request unchanged
        callback(null, request);
    };
    

    Many thanks to @LongZheng for his answer. For some reason his code did not work for me, but it might for some, so check his answer out. Also, big shoutout to Manc, the creator of this lambda-edge-nice-urls repo. My code is basically a mix of both.