sst / sst

Build modern full-stack applications on AWS

Home Page:https://sst.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SsrSite with Lambda@Edge on VIEWER_REQUEST event not deployable

alexbricepalo opened this issue · comments

I tried to add a Lambda@Edge function to a SsrSite construct using the VIEWER_REQUEST event trigger to intercept requests to the Cloudfront Distribution:

const site = new SsrSite(stack, "Site", {
  path: "www/",
  edge: true,
  cdk: {
    distribution: {
      defaultBehavior: {
        edgeLambdas: [
          {
            functionVersion: rewriteLambdaEdgeFunction.currentVersion,
            eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST
          }
        ]
      }
    }
  }
});

Error: Site/Distribution: Resource handler returned message: "Invalid request provided: The event type of a function association must be unique in the cache behavior. Event type viewer-request cannot be associated with two functions

How to Reproduce

  1. Create a clean sst app
  2. Create a frontend stack and add following code:
      const rewriteLambdaEdgeFunction = new cloudfront.experimental.EdgeFunction(stack, 'RewriteLambdaEdgeFunction', {
        runtime: lambda.Runtime.NODEJS_LATEST,
        handler: 'index.handler',
        code: lambda.Code.fromInline(`
          exports.handler = function() {
            return {
              status: "200",
              body: "Hello, World!"
            }
          }
        `),
      });
    
      const site = new SsrSite(stack, "Site", {
        path: "www/",
        edge: true,
        cdk: {
          distribution: {
            defaultBehavior: {
              edgeLambdas: [
                {
                  functionVersion: rewriteLambdaEdgeFunction.currentVersion,
                  eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST
                }
              ]
            }
          }
        }
      });
    

Expected Behavior

The VIEWER_REQUEST event with Lambda@Edge can be set in the cdk distribution default behavior without a conflict with the CloudFront function.

@alexbricepalo the recommended way is to use the cdk.transform prop to prevent the creation of the Cloudfront function. The code would look something like this:

const site = new NextjsSite(stack, "Site", {
  cdk: {
    transform: (plan) => {

      // This tells planning to not create the CF function
      delete plan.cloudFrontFunctions.serverCfFunction;

      // This tells planning to not use CF function in behaviors
      Object.values(plan.behaviors).forEach((behavior) => {
        if (behavior.cfFunction === "serverCfFunction") {
          delete behavior.cfFunction;
        }
      });
    }
  }
});

Ideally, you can create the edge function and hook it up to the behaviors thru plan as well. But there are some limitations w/ plan at the moment. ie. u can't create an edge function with inline code. Reference the code from a file should work instead:

const site = new NextjsSite(stack, "Site", {
  cdk: {
    transform: (plan) => {

      // This tells planning to not create the CF function
      delete plan.cloudFrontFunctions.serverCfFunction;

      // This tells planning to not use CF function in behaviors
      Object.values(plan.behaviors).forEach((behavior) => {
        if (behavior.cfFunction === "serverCfFunction") {
          delete behavior.cfFunction;
        }
      });

+   // This tells planning to create the rewrite Edge function
+   plan.edgeFunctions = {
+     ...plan.edgeFunctions,
+     rewriteFunction: {
+       constructId: "rewriteFunction",
+       function: {
+         runtime: "nodejs20.x",
+         handler: 'index.handler',
+         bundle: "path/to/handler/folder",
+       }
+     }
+   };
    }
  }
});

Excellent, thank you! I updated it a bit for typescript, but it worked for me.