diff --git a/.vscode/settings.json b/.vscode/settings.json index cadf2fcb..c1cb0065 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,4 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, - "eslint.packageManager": "yarn", } \ No newline at end of file diff --git a/API.md b/API.md index 854579ae..b9251509 100644 --- a/API.md +++ b/API.md @@ -26,6 +26,9 @@ new Nextjs(this, 'Web', { }); ``` +## Important Notes +- Due to CloudFront's Distribution Cache Behavior pattern matching limitations, a cache behavior will be created for each top level file or directory in your `public/` folder. CloudFront has a soft limit of [25 cache behaviors per distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-web-distributions). Therefore, it's recommended to include all assets that can be under a top level folder like `public/static/`. Learn more in open-next docs [here](https://github.com/sst/open-next/blob/main/README.md#workaround-create-one-cache-behavior-per-top-level-file-and-folder-in-public-aws-specific). + ## Documentation Available on [Construct Hub](https://constructs.dev/packages/cdk-nextjs-standalone/). @@ -124,6 +127,19 @@ Don't manually update package.json or use npm CLI. Update dependencies in .proje ## Breaking changes +- v4.0.0 + - Renamed `NextjsLambda` to `NextjsServer` + - Renamed `ImageOptimizationLambda` to `NextjsImage` + - Renamed `NextjsCachePolicyProps.lambdaCachePolicy` to `NextjsCachePolicyProps.serverCachePolicy` + - Removed `NextjsOriginRequestPolicyProps.fallbackOriginRequestPolicy` + - Renamed `NextjsOriginRequestPolicyProps.lambdaOriginRequestPolicy` to `NextjsOriginRequestPolicyProps.serverOriginRequestPolicy` + - Removed `NextjsDistribution.staticCachePolicyProps` + - Renamed `NextjsDistribution.lambdaCachePolicyProps` to `NextjsDistribution.serverCachePolicyProps` + - Renamed `NextjsDistribution.lambdaOriginRequestPolicyProps` to `NextjsDistribution.serverOriginRequestPolicyProps` + - Removed `NextjsDistribution.fallbackOriginRequestPolicyProps` + - Removed `NextjsDistribution.imageOptimizationOriginRequestPolicyProps` + - NOTE: when upgrading to v4 from v3, the Lambda@Edge function will be renamed or removed. CloudFormation will fail to delete the function b/c they're replicated a take ~15 min to delete (more [here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-delete-replicas.html)). You can either deploy CloudFormation with it's "no rollback" feature for a clean deployment or mark the Lambda@Edge function as "retain on delete". + - v3.0.0: Using open-next for building, ARM64 architecture for image handling, new build options. - v2.0.0: SST wrapper changed, lambda/assets/distribution defaults now are in the `defaults` prop, refactored distribution settings into the new NextjsDistribution construct. If you are upgrading, you must temporarily remove the `customDomain` on your existing 1.x.x app before upgrading to >=2.x.x because the CloudFront distribution will get recreated due to refactoring, and the custom domain must be globally unique across all CloudFront distributions. Prepare for downtime. @@ -132,41 +148,56 @@ Don't manually update package.json or use npm CLI. Update dependencies in .proje ## Constructs -### ImageOptimizationLambda +### Nextjs + +The `Nextjs` construct is a higher level construct that makes it easy to create a NextJS app. + +Your standalone server application will be bundled using o(utput tracing and will be deployed to a Lambda function. +Static assets will be deployed to an S3 bucket and served via CloudFront. +You must use Next.js 10.3.0 or newer. + +Please provide a `nextjsPath` to the Next.js app inside your project. + +*Example* + +```typescript +new Nextjs(this, "Web", { + nextjsPath: path.resolve("packages/web"), +}) +``` -This lambda handles image optimization. -#### Initializers +#### Initializers ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' +import { Nextjs } from 'cdk-nextjs-standalone' -new ImageOptimizationLambda(scope: Construct, id: string, props: ImageOptimizationProps) +new Nextjs(scope: Construct, id: string, props: NextjsProps) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | ImageOptimizationProps | *No description.* | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsProps | *No description.* | --- -##### `scope`Required +##### `scope`Required - *Type:* constructs.Construct --- -##### `id`Required +##### `id`Required - *Type:* string --- -##### `props`Required +##### `props`Required -- *Type:* ImageOptimizationProps +- *Type:* NextjsProps --- @@ -174,30 +205,11 @@ new ImageOptimizationLambda(scope: Construct, id: string, props: ImageOptimizati | **Name** | **Description** | | --- | --- | -| toString | Returns a string representation of this construct. | -| applyRemovalPolicy | Apply the given removal policy to this resource. | -| addEventSource | Adds an event source to this function. | -| addEventSourceMapping | Adds an event source that maps to this AWS Lambda function. | -| addFunctionUrl | Adds a url to this lambda function. | -| addPermission | Adds a permission to the Lambda resource policy. | -| addToRolePolicy | Adds a statement to the IAM role assumed by the instance. | -| configureAsyncInvoke | Configures options for asynchronous invocation. | -| considerWarningOnInvokeFunctionPermissions | A warning will be added to functions under the following conditions: - permissions that include `lambda:InvokeFunction` are added to the unqualified function. | -| grantInvoke | Grant the given identity permissions to invoke this Lambda. | -| grantInvokeUrl | Grant the given identity permissions to invoke this Lambda Function URL. | -| metric | Return the given named metric for this Function. | -| metricDuration | How long execution of this Lambda takes. | -| metricErrors | How many invocations of this Lambda fail. | -| metricInvocations | How often this Lambda is invoked. | -| metricThrottles | How often this Lambda is throttled. | -| addAlias | Defines an alias for this function. | -| addEnvironment | Adds an environment variable to this Lambda function. | -| addLayers | Adds one or more Lambda Layers to this Lambda function. | -| invalidateVersionBasedOn | Mix additional information into the hash of the Version object. | - ---- - -##### `toString` +| toString | Returns a string representation of this construct. | + +--- + +##### `toString` ```typescript public toString(): string @@ -205,1045 +217,840 @@ public toString(): string Returns a string representation of this construct. -##### `applyRemovalPolicy` +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | + +--- + +##### ~~`isConstruct`~~ ```typescript -public applyRemovalPolicy(policy: RemovalPolicy): void -``` +import { Nextjs } from 'cdk-nextjs-standalone' -Apply the given removal policy to this resource. +Nextjs.isConstruct(x: any) +``` -The Removal Policy controls what happens to this resource when it stops -being managed by CloudFormation, either because you've removed it from the -CDK application or because you've made a change that requires the resource -to be replaced. +Checks if `x` is a construct. -The resource can be deleted (`RemovalPolicy.DESTROY`), or left in your AWS -account for data recovery and cleanup later (`RemovalPolicy.RETAIN`). +###### `x`Required -###### `policy`Required +- *Type:* any -- *Type:* aws-cdk-lib.RemovalPolicy +Any object. --- -##### `addEventSource` +#### Properties -```typescript -public addEventSource(source: IEventSource): void -``` +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| bucket | aws-cdk-lib.aws_s3.IBucket | *No description.* | +| url | string | *No description.* | +| assetsDeployment | NextJsAssetsDeployment | Asset deployment to S3. | +| distribution | NextjsDistribution | CloudFront distribution. | +| imageOptimizationFunction | NextjsImage | The image optimization handler lambda function. | +| imageOptimizationLambdaFunctionUrl | aws-cdk-lib.aws_lambda.FunctionUrl | *No description.* | +| lambdaFunctionUrl | aws-cdk-lib.aws_lambda.FunctionUrl | *No description.* | +| nextBuild | NextjsBuild | Built NextJS project output. | +| revalidation | NextjsRevalidation | Revalidation handler and queue. | +| serverFunction | NextjsServer | The main NextJS server handler lambda function. | +| tempBuildDir | string | Where build-time assets for deployment are stored. | +| configBucket | aws-cdk-lib.aws_s3.Bucket | *No description.* | -Adds an event source to this function. +--- -Event sources are implemented in the @aws-cdk/aws-lambda-event-sources module. +##### `node`Required -The following example adds an SQS Queue as an event source: -``` -import { SqsEventSource } from '@aws-cdk/aws-lambda-event-sources'; -myFunction.addEventSource(new SqsEventSource(myQueue)); +```typescript +public readonly node: Node; ``` -###### `source`Required +- *Type:* constructs.Node -- *Type:* aws-cdk-lib.aws_lambda.IEventSource +The tree node. --- -##### `addEventSourceMapping` +##### `bucket`Required ```typescript -public addEventSourceMapping(id: string, options: EventSourceMappingOptions): EventSourceMapping +public readonly bucket: IBucket; ``` -Adds an event source that maps to this AWS Lambda function. - -###### `id`Required - -- *Type:* string +- *Type:* aws-cdk-lib.aws_s3.IBucket --- -###### `options`Required +##### `url`Required -- *Type:* aws-cdk-lib.aws_lambda.EventSourceMappingOptions +```typescript +public readonly url: string; +``` + +- *Type:* string --- -##### `addFunctionUrl` +##### `assetsDeployment`Required ```typescript -public addFunctionUrl(options?: FunctionUrlOptions): FunctionUrl +public readonly assetsDeployment: NextJsAssetsDeployment; ``` -Adds a url to this lambda function. - -###### `options`Optional +- *Type:* NextJsAssetsDeployment -- *Type:* aws-cdk-lib.aws_lambda.FunctionUrlOptions +Asset deployment to S3. --- -##### `addPermission` +##### `distribution`Required ```typescript -public addPermission(id: string, permission: Permission): void +public readonly distribution: NextjsDistribution; ``` -Adds a permission to the Lambda resource policy. - -> [Permission for details.](Permission for details.) - -###### `id`Required - -- *Type:* string +- *Type:* NextjsDistribution -The id for the permission construct. +CloudFront distribution. --- -###### `permission`Required +##### `imageOptimizationFunction`Required -- *Type:* aws-cdk-lib.aws_lambda.Permission +```typescript +public readonly imageOptimizationFunction: NextjsImage; +``` -The permission to grant to this Lambda function. +- *Type:* NextjsImage + +The image optimization handler lambda function. --- -##### `addToRolePolicy` +##### `imageOptimizationLambdaFunctionUrl`Required ```typescript -public addToRolePolicy(statement: PolicyStatement): void +public readonly imageOptimizationLambdaFunctionUrl: FunctionUrl; ``` -Adds a statement to the IAM role assumed by the instance. - -###### `statement`Required - -- *Type:* aws-cdk-lib.aws_iam.PolicyStatement +- *Type:* aws-cdk-lib.aws_lambda.FunctionUrl --- -##### `configureAsyncInvoke` +##### `lambdaFunctionUrl`Required ```typescript -public configureAsyncInvoke(options: EventInvokeConfigOptions): void +public readonly lambdaFunctionUrl: FunctionUrl; ``` -Configures options for asynchronous invocation. - -###### `options`Required - -- *Type:* aws-cdk-lib.aws_lambda.EventInvokeConfigOptions +- *Type:* aws-cdk-lib.aws_lambda.FunctionUrl --- -##### `considerWarningOnInvokeFunctionPermissions` +##### `nextBuild`Required ```typescript -public considerWarningOnInvokeFunctionPermissions(scope: Construct, action: string): void +public readonly nextBuild: NextjsBuild; ``` -A warning will be added to functions under the following conditions: - permissions that include `lambda:InvokeFunction` are added to the unqualified function. - -function.currentVersion is invoked before or after the permission is created. +- *Type:* NextjsBuild -This applies only to permissions on Lambda functions, not versions or aliases. -This function is overridden as a noOp for QualifiedFunctionBase. +Built NextJS project output. -###### `scope`Required +--- -- *Type:* constructs.Construct +##### `revalidation`Required ---- +```typescript +public readonly revalidation: NextjsRevalidation; +``` -###### `action`Required +- *Type:* NextjsRevalidation -- *Type:* string +Revalidation handler and queue. --- -##### `grantInvoke` +##### `serverFunction`Required ```typescript -public grantInvoke(grantee: IGrantable): Grant +public readonly serverFunction: NextjsServer; ``` -Grant the given identity permissions to invoke this Lambda. - -###### `grantee`Required +- *Type:* NextjsServer -- *Type:* aws-cdk-lib.aws_iam.IGrantable +The main NextJS server handler lambda function. --- -##### `grantInvokeUrl` +##### `tempBuildDir`Required ```typescript -public grantInvokeUrl(grantee: IGrantable): Grant +public readonly tempBuildDir: string; ``` -Grant the given identity permissions to invoke this Lambda Function URL. - -###### `grantee`Required +- *Type:* string -- *Type:* aws-cdk-lib.aws_iam.IGrantable +Where build-time assets for deployment are stored. --- -##### `metric` +##### `configBucket`Optional ```typescript -public metric(metricName: string, props?: MetricOptions): Metric +public readonly configBucket: Bucket; ``` -Return the given named metric for this Function. - -###### `metricName`Required - -- *Type:* string +- *Type:* aws-cdk-lib.aws_s3.Bucket --- -###### `props`Optional -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +### NextJsAssetsDeployment ---- +Uploads NextJS-built static and public files to S3. + +Will rewrite CloudFormation references with their resolved values after uploading. -##### `metricDuration` +#### Initializers ```typescript -public metricDuration(props?: MetricOptions): Metric +import { NextJsAssetsDeployment } from 'cdk-nextjs-standalone' + +new NextJsAssetsDeployment(scope: Construct, id: string, props: NextjsAssetsDeploymentProps) ``` -How long execution of this Lambda takes. +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsAssetsDeploymentProps | *No description.* | -Average over 5 minutes +--- -###### `props`Optional +##### `scope`Required -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +- *Type:* constructs.Construct --- -##### `metricErrors` +##### `id`Required -```typescript -public metricErrors(props?: MetricOptions): Metric -``` +- *Type:* string -How many invocations of this Lambda fail. +--- -Sum over 5 minutes +##### `props`Required -###### `props`Optional +- *Type:* NextjsAssetsDeploymentProps -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | --- -##### `metricInvocations` +##### `toString` ```typescript -public metricInvocations(props?: MetricOptions): Metric +public toString(): string ``` -How often this Lambda is invoked. - -Sum over 5 minutes +Returns a string representation of this construct. -###### `props`Optional +#### Static Functions -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | --- -##### `metricThrottles` +##### ~~`isConstruct`~~ ```typescript -public metricThrottles(props?: MetricOptions): Metric +import { NextJsAssetsDeployment } from 'cdk-nextjs-standalone' + +NextJsAssetsDeployment.isConstruct(x: any) ``` -How often this Lambda is throttled. +Checks if `x` is a construct. -Sum over 5 minutes +###### `x`Required -###### `props`Optional +- *Type:* any -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +Any object. --- -##### `addAlias` +#### Properties -```typescript -public addAlias(aliasName: string, options?: AliasOptions): Alias -``` - -Defines an alias for this function. - -The alias will automatically be updated to point to the latest version of -the function as it is being updated during a deployment. - -```ts -declare const fn: lambda.Function; - -fn.addAlias('Live'); - -// Is equivalent to - -new lambda.Alias(this, 'AliasLive', { - aliasName: 'Live', - version: fn.currentVersion, -}); -``` - -###### `aliasName`Required - -- *Type:* string - -The name of the alias. - ---- - -###### `options`Optional - -- *Type:* aws-cdk-lib.aws_lambda.AliasOptions - -Alias options. +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| bucket | aws-cdk-lib.aws_s3.IBucket | Bucket containing assets. | +| deployments | aws-cdk-lib.aws_s3_deployment.BucketDeployment[] | Asset deployments to S3. | +| staticTempDir | string | *No description.* | +| rewriter | NextjsS3EnvRewriter | *No description.* | --- -##### `addEnvironment` +##### `node`Required ```typescript -public addEnvironment(key: string, value: string, options?: EnvironmentOptions): Function +public readonly node: Node; ``` -Adds an environment variable to this Lambda function. - -If this is a ref to a Lambda function, this operation results in a no-op. - -###### `key`Required - -- *Type:* string - -The environment variable key. - ---- - -###### `value`Required - -- *Type:* string - -The environment variable's value. - ---- - -###### `options`Optional - -- *Type:* aws-cdk-lib.aws_lambda.EnvironmentOptions +- *Type:* constructs.Node -Environment variable options. +The tree node. --- -##### `addLayers` +##### `bucket`Required ```typescript -public addLayers(layers: ILayerVersion): void +public readonly bucket: IBucket; ``` -Adds one or more Lambda Layers to this Lambda function. - -###### `layers`Required - -- *Type:* aws-cdk-lib.aws_lambda.ILayerVersion +- *Type:* aws-cdk-lib.aws_s3.IBucket -the layers to be added. +Bucket containing assets. --- -##### `invalidateVersionBasedOn` +##### `deployments`Required ```typescript -public invalidateVersionBasedOn(x: string): void +public readonly deployments: BucketDeployment[]; ``` -Mix additional information into the hash of the Version object. - -The Lambda Function construct does its best to automatically create a new -Version when anything about the Function changes (its code, its layers, -any of the other properties). - -However, you can sometimes source information from places that the CDK cannot -look into, like the deploy-time values of SSM parameters. In those cases, -the CDK would not force the creation of a new Version object when it actually -should. - -This method can be used to invalidate the current Version object. Pass in -any string into this method, and make sure the string changes when you know -a new Version needs to be created. - -This method may be called more than once. - -###### `x`Required - -- *Type:* string - ---- - -#### Static Functions +- *Type:* aws-cdk-lib.aws_s3_deployment.BucketDeployment[] -| **Name** | **Description** | -| --- | --- | -| isConstruct | Checks if `x` is a construct. | -| isOwnedResource | Returns true if the construct was created by CDK, and false otherwise. | -| isResource | Check whether the given construct is a Resource. | -| classifyVersionProperty | Record whether specific properties in the `AWS::Lambda::Function` resource should also be associated to the Version resource. | -| fromFunctionArn | Import a lambda function into the CDK using its ARN. | -| fromFunctionAttributes | Creates a Lambda function object which represents a function not defined within this stack. | -| fromFunctionName | Import a lambda function into the CDK using its name. | -| metricAll | Return the given named metric for this Lambda. | -| metricAllConcurrentExecutions | Metric for the number of concurrent executions across all Lambdas. | -| metricAllDuration | Metric for the Duration executing all Lambdas. | -| metricAllErrors | Metric for the number of Errors executing all Lambdas. | -| metricAllInvocations | Metric for the number of invocations of all Lambdas. | -| metricAllThrottles | Metric for the number of throttled invocations of all Lambdas. | -| metricAllUnreservedConcurrentExecutions | Metric for the number of unreserved concurrent executions across all Lambdas. | +Asset deployments to S3. --- -##### ~~`isConstruct`~~ +##### `staticTempDir`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.isConstruct(x: any) +public readonly staticTempDir: string; ``` -Checks if `x` is a construct. - -###### `x`Required - -- *Type:* any - -Any object. +- *Type:* string --- -##### `isOwnedResource` +##### `rewriter`Optional ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.isOwnedResource(construct: IConstruct) +public readonly rewriter: NextjsS3EnvRewriter; ``` -Returns true if the construct was created by CDK, and false otherwise. - -###### `construct`Required - -- *Type:* constructs.IConstruct +- *Type:* NextjsS3EnvRewriter --- -##### `isResource` -```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.isResource(construct: IConstruct) -``` - -Check whether the given construct is a Resource. - -###### `construct`Required +### NextjsBuild -- *Type:* constructs.IConstruct +Represents a built NextJS application. ---- +This construct runs `npm build` in standalone output mode inside your `nextjsPath`. +This construct can be used by higher level constructs or used directly. -##### `classifyVersionProperty` +#### Initializers ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' +import { NextjsBuild } from 'cdk-nextjs-standalone' -ImageOptimizationLambda.classifyVersionProperty(propertyName: string, locked: boolean) +new NextjsBuild(scope: Construct, id: string, props: NextjsBuildProps) ``` -Record whether specific properties in the `AWS::Lambda::Function` resource should also be associated to the Version resource. - -See 'currentVersion' section in the module README for more details. - -###### `propertyName`Required - -- *Type:* string - -The property to classify. - ---- - -###### `locked`Required - -- *Type:* boolean - -whether the property should be associated to the version or not. +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsBuildProps | *No description.* | --- -##### `fromFunctionArn` - -```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.fromFunctionArn(scope: Construct, id: string, functionArn: string) -``` - -Import a lambda function into the CDK using its ARN. - -###### `scope`Required +##### `scope`Required - *Type:* constructs.Construct --- -###### `id`Required +##### `id`Required - *Type:* string --- -###### `functionArn`Required +##### `props`Required -- *Type:* string +- *Type:* NextjsBuildProps --- -##### `fromFunctionAttributes` - -```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.fromFunctionAttributes(scope: Construct, id: string, attrs: FunctionAttributes) -``` - -Creates a Lambda function object which represents a function not defined within this stack. - -###### `scope`Required - -- *Type:* constructs.Construct +#### Methods -The parent construct. +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | +| readPublicFileList | *No description.* | --- -###### `id`Required +##### `toString` -- *Type:* string +```typescript +public toString(): string +``` -The name of the lambda construct. +Returns a string representation of this construct. ---- +##### `readPublicFileList` -###### `attrs`Required +```typescript +public readPublicFileList(): string[] +``` -- *Type:* aws-cdk-lib.aws_lambda.FunctionAttributes +#### Static Functions -the attributes of the function to import. +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | --- -##### `fromFunctionName` +##### ~~`isConstruct`~~ ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' +import { NextjsBuild } from 'cdk-nextjs-standalone' -ImageOptimizationLambda.fromFunctionName(scope: Construct, id: string, functionName: string) +NextjsBuild.isConstruct(x: any) ``` -Import a lambda function into the CDK using its name. - -###### `scope`Required - -- *Type:* constructs.Construct +Checks if `x` is a construct. ---- +###### `x`Required -###### `id`Required +- *Type:* any -- *Type:* string +Any object. --- -###### `functionName`Required +#### Properties -- *Type:* string +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| nextCacheDir | string | Cache directory for generated data. | +| nextImageFnDir | string | Contains function for processessing image requests. | +| nextRevalidateFnDir | string | Contains function for processing items from revalidation queue. | +| nextServerFnDir | string | Contains server code and dependencies. | +| nextStaticDir | string | Static files containing client-side code. | +| projectRoot | string | *No description.* | +| props | NextjsBuildProps | *No description.* | +| nextMiddlewareFnDir | string | Contains code for middleware. | --- -##### `metricAll` +##### `node`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAll(metricName: string, props?: MetricOptions) +public readonly node: Node; ``` -Return the given named metric for this Lambda. - -###### `metricName`Required - -- *Type:* string - ---- - -###### `props`Optional +- *Type:* constructs.Node -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +The tree node. --- -##### `metricAllConcurrentExecutions` +##### `nextCacheDir`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAllConcurrentExecutions(props?: MetricOptions) +public readonly nextCacheDir: string; ``` -Metric for the number of concurrent executions across all Lambdas. - -###### `props`Optional +- *Type:* string -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +Cache directory for generated data. --- -##### `metricAllDuration` +##### `nextImageFnDir`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAllDuration(props?: MetricOptions) +public readonly nextImageFnDir: string; ``` -Metric for the Duration executing all Lambdas. +- *Type:* string -###### `props`Optional +Contains function for processessing image requests. -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +Should be arm64. --- -##### `metricAllErrors` +##### `nextRevalidateFnDir`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAllErrors(props?: MetricOptions) +public readonly nextRevalidateFnDir: string; ``` -Metric for the number of Errors executing all Lambdas. - -###### `props`Optional +- *Type:* string -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +Contains function for processing items from revalidation queue. --- -##### `metricAllInvocations` +##### `nextServerFnDir`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAllInvocations(props?: MetricOptions) +public readonly nextServerFnDir: string; ``` -Metric for the number of invocations of all Lambdas. - -###### `props`Optional +- *Type:* string -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +Contains server code and dependencies. --- -##### `metricAllThrottles` +##### `nextStaticDir`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAllThrottles(props?: MetricOptions) +public readonly nextStaticDir: string; ``` -Metric for the number of throttled invocations of all Lambdas. - -###### `props`Optional +- *Type:* string -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +Static files containing client-side code. --- -##### `metricAllUnreservedConcurrentExecutions` +##### `projectRoot`Required ```typescript -import { ImageOptimizationLambda } from 'cdk-nextjs-standalone' - -ImageOptimizationLambda.metricAllUnreservedConcurrentExecutions(props?: MetricOptions) +public readonly projectRoot: string; ``` -Metric for the number of unreserved concurrent executions across all Lambdas. - -###### `props`Optional - -- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions +- *Type:* string --- -#### Properties - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| node | constructs.Node | The tree node. | -| env | aws-cdk-lib.ResourceEnvironment | The environment this resource belongs to. | -| stack | aws-cdk-lib.Stack | The stack in which this resource is defined. | -| architecture | aws-cdk-lib.aws_lambda.Architecture | The architecture of this Lambda Function (this is an optional attribute and defaults to X86_64). | -| connections | aws-cdk-lib.aws_ec2.Connections | Access the Connections object. | -| functionArn | string | ARN of this function. | -| functionName | string | Name of this function. | -| grantPrincipal | aws-cdk-lib.aws_iam.IPrincipal | The principal this Lambda Function is running as. | -| isBoundToVpc | boolean | Whether or not this Lambda function was bound to a VPC. | -| latestVersion | aws-cdk-lib.aws_lambda.IVersion | The `$LATEST` version of this function. | -| permissionsNode | constructs.Node | The construct node where permissions are attached. | -| resourceArnsForGrantInvoke | string[] | The ARN(s) to put into the resource field of the generated IAM policy for grantInvoke(). | -| role | aws-cdk-lib.aws_iam.IRole | Execution role associated with this function. | -| currentVersion | aws-cdk-lib.aws_lambda.Version | Returns a `lambda.Version` which represents the current version of this Lambda function. A new version will be created every time the function's configuration changes. | -| logGroup | aws-cdk-lib.aws_logs.ILogGroup | The LogGroup where the Lambda function's logs are made available. | -| runtime | aws-cdk-lib.aws_lambda.Runtime | The runtime configured for this lambda. | -| deadLetterQueue | aws-cdk-lib.aws_sqs.IQueue | The DLQ (as queue) associated with this Lambda Function (this is an optional attribute). | -| deadLetterTopic | aws-cdk-lib.aws_sns.ITopic | The DLQ (as topic) associated with this Lambda Function (this is an optional attribute). | -| timeout | aws-cdk-lib.Duration | The timeout configured for this lambda. | -| bucket | aws-cdk-lib.aws_s3.IBucket | *No description.* | - ---- - -##### `node`Required +##### `props`Required ```typescript -public readonly node: Node; +public readonly props: NextjsBuildProps; ``` -- *Type:* constructs.Node - -The tree node. +- *Type:* NextjsBuildProps --- -##### `env`Required +##### `nextMiddlewareFnDir`Optional ```typescript -public readonly env: ResourceEnvironment; +public readonly nextMiddlewareFnDir: string; ``` -- *Type:* aws-cdk-lib.ResourceEnvironment +- *Type:* string -The environment this resource belongs to. +Contains code for middleware. -For resources that are created and managed by the CDK -(generally, those created by creating new class instances like Role, Bucket, etc.), -this is always the same as the environment of the stack they belong to; -however, for imported resources -(those obtained from static methods like fromRoleArn, fromBucketName, etc.), -that might be different than the stack they were imported into. +Not currently used. --- -##### `stack`Required + +### NextjsDistribution + +Create a CloudFront distribution to serve a Next.js application. + +#### Initializers ```typescript -public readonly stack: Stack; -``` +import { NextjsDistribution } from 'cdk-nextjs-standalone' -- *Type:* aws-cdk-lib.Stack +new NextjsDistribution(scope: Construct, id: string, props: NextjsDistributionProps) +``` -The stack in which this resource is defined. +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsDistributionProps | *No description.* | --- -##### `architecture`Required +##### `scope`Required -```typescript -public readonly architecture: Architecture; -``` +- *Type:* constructs.Construct -- *Type:* aws-cdk-lib.aws_lambda.Architecture +--- -The architecture of this Lambda Function (this is an optional attribute and defaults to X86_64). +##### `id`Required + +- *Type:* string --- -##### `connections`Required +##### `props`Required -```typescript -public readonly connections: Connections; -``` +- *Type:* NextjsDistributionProps -- *Type:* aws-cdk-lib.aws_ec2.Connections +--- -Access the Connections object. +#### Methods -Will fail if not a VPC-enabled Lambda Function +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | --- -##### `functionArn`Required +##### `toString` ```typescript -public readonly functionArn: string; +public toString(): string ``` -- *Type:* string +Returns a string representation of this construct. -ARN of this function. +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | --- -##### `functionName`Required +##### ~~`isConstruct`~~ ```typescript -public readonly functionName: string; +import { NextjsDistribution } from 'cdk-nextjs-standalone' + +NextjsDistribution.isConstruct(x: any) ``` -- *Type:* string +Checks if `x` is a construct. -Name of this function. +###### `x`Required ---- +- *Type:* any -##### `grantPrincipal`Required +Any object. -```typescript -public readonly grantPrincipal: IPrincipal; -``` +--- -- *Type:* aws-cdk-lib.aws_iam.IPrincipal +#### Properties -The principal this Lambda Function is running as. +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| imageCachePolicyProps | aws-cdk-lib.aws_cloudfront.CachePolicyProps | The default CloudFront Cache Policy properties for images. | +| serverCachePolicyProps | aws-cdk-lib.aws_cloudfront.CachePolicyProps | The default CloudFront cache policy properties for dynamic requests to server handler. | +| distributionDomain | string | The domain name of the internally created CloudFront Distribution. | +| distributionId | string | The ID of the internally created CloudFront Distribution. | +| url | string | The CloudFront URL of the website. | +| customDomainName | string | *No description.* | +| customDomainUrl | string | If the custom domain is enabled, this is the URL of the website with the custom domain. | +| distribution | aws-cdk-lib.aws_cloudfront.Distribution | The internally created CloudFront `Distribution` instance. | +| tempBuildDir | string | *No description.* | +| certificate | aws-cdk-lib.aws_certificatemanager.ICertificate | The AWS Certificate Manager certificate for the custom domain. | +| hostedZone | aws-cdk-lib.aws_route53.IHostedZone | The Route 53 hosted zone for the custom domain. | --- -##### `isBoundToVpc`Required +##### `node`Required ```typescript -public readonly isBoundToVpc: boolean; +public readonly node: Node; ``` -- *Type:* boolean - -Whether or not this Lambda function was bound to a VPC. +- *Type:* constructs.Node -If this is is `false`, trying to access the `connections` object will fail. +The tree node. --- -##### `latestVersion`Required +##### `imageCachePolicyProps`Required ```typescript -public readonly latestVersion: IVersion; +public readonly imageCachePolicyProps: CachePolicyProps; ``` -- *Type:* aws-cdk-lib.aws_lambda.IVersion - -The `$LATEST` version of this function. - -Note that this is reference to a non-specific AWS Lambda version, which -means the function this version refers to can return different results in -different invocations. +- *Type:* aws-cdk-lib.aws_cloudfront.CachePolicyProps -To obtain a reference to an explicit version which references the current -function configuration, use `lambdaFunction.currentVersion` instead. +The default CloudFront Cache Policy properties for images. --- -##### `permissionsNode`Required +##### `serverCachePolicyProps`Required ```typescript -public readonly permissionsNode: Node; +public readonly serverCachePolicyProps: CachePolicyProps; ``` -- *Type:* constructs.Node +- *Type:* aws-cdk-lib.aws_cloudfront.CachePolicyProps -The construct node where permissions are attached. +The default CloudFront cache policy properties for dynamic requests to server handler. --- -##### `resourceArnsForGrantInvoke`Required +##### `distributionDomain`Required ```typescript -public readonly resourceArnsForGrantInvoke: string[]; +public readonly distributionDomain: string; ``` -- *Type:* string[] +- *Type:* string -The ARN(s) to put into the resource field of the generated IAM policy for grantInvoke(). +The domain name of the internally created CloudFront Distribution. --- -##### `role`Optional +##### `distributionId`Required ```typescript -public readonly role: IRole; +public readonly distributionId: string; ``` -- *Type:* aws-cdk-lib.aws_iam.IRole +- *Type:* string -Execution role associated with this function. +The ID of the internally created CloudFront Distribution. --- -##### `currentVersion`Required +##### `url`Required ```typescript -public readonly currentVersion: Version; +public readonly url: string; ``` -- *Type:* aws-cdk-lib.aws_lambda.Version - -Returns a `lambda.Version` which represents the current version of this Lambda function. A new version will be created every time the function's configuration changes. +- *Type:* string -You can specify options for this version using the `currentVersionOptions` -prop when initializing the `lambda.Function`. +The CloudFront URL of the website. --- -##### `logGroup`Required +##### `customDomainName`Optional ```typescript -public readonly logGroup: ILogGroup; +public readonly customDomainName: string; ``` -- *Type:* aws-cdk-lib.aws_logs.ILogGroup - -The LogGroup where the Lambda function's logs are made available. - -If either `logRetention` is set or this property is called, a CloudFormation custom resource is added to the stack that -pre-creates the log group as part of the stack deployment, if it already doesn't exist, and sets the correct log retention -period (never expire, by default). - -Further, if the log group already exists and the `logRetention` is not set, the custom resource will reset the log retention -to never expire even if it was configured with a different value. +- *Type:* string --- -##### `runtime`Required +##### `customDomainUrl`Optional ```typescript -public readonly runtime: Runtime; +public readonly customDomainUrl: string; ``` -- *Type:* aws-cdk-lib.aws_lambda.Runtime +- *Type:* string -The runtime configured for this lambda. +If the custom domain is enabled, this is the URL of the website with the custom domain. --- -##### `deadLetterQueue`Optional +##### `distribution`Required ```typescript -public readonly deadLetterQueue: IQueue; +public readonly distribution: Distribution; ``` -- *Type:* aws-cdk-lib.aws_sqs.IQueue +- *Type:* aws-cdk-lib.aws_cloudfront.Distribution -The DLQ (as queue) associated with this Lambda Function (this is an optional attribute). +The internally created CloudFront `Distribution` instance. --- -##### `deadLetterTopic`Optional +##### `tempBuildDir`Required ```typescript -public readonly deadLetterTopic: ITopic; +public readonly tempBuildDir: string; ``` -- *Type:* aws-cdk-lib.aws_sns.ITopic - -The DLQ (as topic) associated with this Lambda Function (this is an optional attribute). +- *Type:* string --- -##### `timeout`Optional +##### `certificate`Optional ```typescript -public readonly timeout: Duration; +public readonly certificate: ICertificate; ``` -- *Type:* aws-cdk-lib.Duration +- *Type:* aws-cdk-lib.aws_certificatemanager.ICertificate -The timeout configured for this lambda. +The AWS Certificate Manager certificate for the custom domain. --- -##### `bucket`Required +##### `hostedZone`Optional ```typescript -public readonly bucket: IBucket; +public readonly hostedZone: IHostedZone; ``` -- *Type:* aws-cdk-lib.aws_s3.IBucket - ---- - - -### Nextjs - -The `Nextjs` construct is a higher level construct that makes it easy to create a NextJS app. +- *Type:* aws-cdk-lib.aws_route53.IHostedZone -Your standalone server application will be bundled using o(utput tracing and will be deployed to a Lambda function. -Static assets will be deployed to an S3 bucket and served via CloudFront. -You must use Next.js 10.3.0 or newer. +The Route 53 hosted zone for the custom domain. -Please provide a `nextjsPath` to the Next.js app inside your project. +--- -*Example* -```typescript -new Nextjs(this, "Web", { - nextjsPath: path.resolve("packages/web"), -}) -``` +### NextjsImage +This lambda handles image optimization. -#### Initializers +#### Initializers ```typescript -import { Nextjs } from 'cdk-nextjs-standalone' +import { NextjsImage } from 'cdk-nextjs-standalone' -new Nextjs(scope: Construct, id: string, props: NextjsProps) +new NextjsImage(scope: Construct, id: string, props: NextjsImageProps) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | NextjsProps | *No description.* | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsImageProps | *No description.* | --- -##### `scope`Required +##### `scope`Required - *Type:* constructs.Construct --- -##### `id`Required +##### `id`Required - *Type:* string --- -##### `props`Required +##### `props`Required -- *Type:* NextjsProps +- *Type:* NextjsImageProps --- @@ -1251,11 +1058,30 @@ new Nextjs(scope: Construct, id: string, props: NextjsProps) | **Name** | **Description** | | --- | --- | -| toString | Returns a string representation of this construct. | - ---- - -##### `toString` +| toString | Returns a string representation of this construct. | +| applyRemovalPolicy | Apply the given removal policy to this resource. | +| addEventSource | Adds an event source to this function. | +| addEventSourceMapping | Adds an event source that maps to this AWS Lambda function. | +| addFunctionUrl | Adds a url to this lambda function. | +| addPermission | Adds a permission to the Lambda resource policy. | +| addToRolePolicy | Adds a statement to the IAM role assumed by the instance. | +| configureAsyncInvoke | Configures options for asynchronous invocation. | +| considerWarningOnInvokeFunctionPermissions | A warning will be added to functions under the following conditions: - permissions that include `lambda:InvokeFunction` are added to the unqualified function. | +| grantInvoke | Grant the given identity permissions to invoke this Lambda. | +| grantInvokeUrl | Grant the given identity permissions to invoke this Lambda Function URL. | +| metric | Return the given named metric for this Function. | +| metricDuration | How long execution of this Lambda takes. | +| metricErrors | How many invocations of this Lambda fail. | +| metricInvocations | How often this Lambda is invoked. | +| metricThrottles | How often this Lambda is throttled. | +| addAlias | Defines an alias for this function. | +| addEnvironment | Adds an environment variable to this Lambda function. | +| addLayers | Adds one or more Lambda Layers to this Lambda function. | +| invalidateVersionBasedOn | Mix additional information into the hash of the Version object. | + +--- + +##### `toString` ```typescript public toString(): string @@ -1263,433 +1089,426 @@ public toString(): string Returns a string representation of this construct. -#### Static Functions - -| **Name** | **Description** | -| --- | --- | -| isConstruct | Checks if `x` is a construct. | - ---- - -##### ~~`isConstruct`~~ +##### `applyRemovalPolicy` ```typescript -import { Nextjs } from 'cdk-nextjs-standalone' - -Nextjs.isConstruct(x: any) +public applyRemovalPolicy(policy: RemovalPolicy): void ``` -Checks if `x` is a construct. - -###### `x`Required - -- *Type:* any - -Any object. - ---- - -#### Properties - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| node | constructs.Node | The tree node. | -| bucket | aws-cdk-lib.aws_s3.IBucket | *No description.* | -| url | string | *No description.* | -| assetsDeployment | NextJsAssetsDeployment | Asset deployment to S3. | -| distribution | NextjsDistribution | CloudFront distribution. | -| imageOptimizationFunction | ImageOptimizationLambda | The image optimization handler lambda function. | -| imageOptimizationLambdaFunctionUrl | aws-cdk-lib.aws_lambda.FunctionUrl | *No description.* | -| lambdaFunctionUrl | aws-cdk-lib.aws_lambda.FunctionUrl | *No description.* | -| nextBuild | NextjsBuild | Built NextJS project output. | -| revalidation | NextjsRevalidation | Revalidation handler and queue. | -| serverFunction | NextJsLambda | The main NextJS server handler lambda function. | -| tempBuildDir | string | Where build-time assets for deployment are stored. | -| configBucket | aws-cdk-lib.aws_s3.Bucket | *No description.* | - ---- +Apply the given removal policy to this resource. -##### `node`Required +The Removal Policy controls what happens to this resource when it stops +being managed by CloudFormation, either because you've removed it from the +CDK application or because you've made a change that requires the resource +to be replaced. -```typescript -public readonly node: Node; -``` +The resource can be deleted (`RemovalPolicy.DESTROY`), or left in your AWS +account for data recovery and cleanup later (`RemovalPolicy.RETAIN`). -- *Type:* constructs.Node +###### `policy`Required -The tree node. +- *Type:* aws-cdk-lib.RemovalPolicy --- -##### `bucket`Required +##### `addEventSource` ```typescript -public readonly bucket: IBucket; +public addEventSource(source: IEventSource): void ``` -- *Type:* aws-cdk-lib.aws_s3.IBucket - ---- +Adds an event source to this function. -##### `url`Required +Event sources are implemented in the @aws-cdk/aws-lambda-event-sources module. -```typescript -public readonly url: string; +The following example adds an SQS Queue as an event source: +``` +import { SqsEventSource } from '@aws-cdk/aws-lambda-event-sources'; +myFunction.addEventSource(new SqsEventSource(myQueue)); ``` -- *Type:* string +###### `source`Required + +- *Type:* aws-cdk-lib.aws_lambda.IEventSource --- -##### `assetsDeployment`Required +##### `addEventSourceMapping` ```typescript -public readonly assetsDeployment: NextJsAssetsDeployment; +public addEventSourceMapping(id: string, options: EventSourceMappingOptions): EventSourceMapping ``` -- *Type:* NextJsAssetsDeployment - -Asset deployment to S3. +Adds an event source that maps to this AWS Lambda function. ---- +###### `id`Required -##### `distribution`Required +- *Type:* string -```typescript -public readonly distribution: NextjsDistribution; -``` +--- -- *Type:* NextjsDistribution +###### `options`Required -CloudFront distribution. +- *Type:* aws-cdk-lib.aws_lambda.EventSourceMappingOptions --- -##### `imageOptimizationFunction`Required +##### `addFunctionUrl` ```typescript -public readonly imageOptimizationFunction: ImageOptimizationLambda; +public addFunctionUrl(options?: FunctionUrlOptions): FunctionUrl ``` -- *Type:* ImageOptimizationLambda +Adds a url to this lambda function. -The image optimization handler lambda function. +###### `options`Optional + +- *Type:* aws-cdk-lib.aws_lambda.FunctionUrlOptions --- -##### `imageOptimizationLambdaFunctionUrl`Required +##### `addPermission` ```typescript -public readonly imageOptimizationLambdaFunctionUrl: FunctionUrl; +public addPermission(id: string, permission: Permission): void ``` -- *Type:* aws-cdk-lib.aws_lambda.FunctionUrl +Adds a permission to the Lambda resource policy. + +> [Permission for details.](Permission for details.) + +###### `id`Required + +- *Type:* string + +The id for the permission construct. --- -##### `lambdaFunctionUrl`Required +###### `permission`Required -```typescript -public readonly lambdaFunctionUrl: FunctionUrl; -``` +- *Type:* aws-cdk-lib.aws_lambda.Permission -- *Type:* aws-cdk-lib.aws_lambda.FunctionUrl +The permission to grant to this Lambda function. --- -##### `nextBuild`Required +##### `addToRolePolicy` ```typescript -public readonly nextBuild: NextjsBuild; +public addToRolePolicy(statement: PolicyStatement): void ``` -- *Type:* NextjsBuild +Adds a statement to the IAM role assumed by the instance. -Built NextJS project output. +###### `statement`Required + +- *Type:* aws-cdk-lib.aws_iam.PolicyStatement --- -##### `revalidation`Required +##### `configureAsyncInvoke` ```typescript -public readonly revalidation: NextjsRevalidation; +public configureAsyncInvoke(options: EventInvokeConfigOptions): void ``` -- *Type:* NextjsRevalidation +Configures options for asynchronous invocation. -Revalidation handler and queue. +###### `options`Required + +- *Type:* aws-cdk-lib.aws_lambda.EventInvokeConfigOptions --- -##### `serverFunction`Required +##### `considerWarningOnInvokeFunctionPermissions` ```typescript -public readonly serverFunction: NextJsLambda; +public considerWarningOnInvokeFunctionPermissions(scope: Construct, action: string): void ``` -- *Type:* NextJsLambda +A warning will be added to functions under the following conditions: - permissions that include `lambda:InvokeFunction` are added to the unqualified function. + +function.currentVersion is invoked before or after the permission is created. -The main NextJS server handler lambda function. +This applies only to permissions on Lambda functions, not versions or aliases. +This function is overridden as a noOp for QualifiedFunctionBase. ---- +###### `scope`Required -##### `tempBuildDir`Required +- *Type:* constructs.Construct -```typescript -public readonly tempBuildDir: string; -``` +--- -- *Type:* string +###### `action`Required -Where build-time assets for deployment are stored. +- *Type:* string --- -##### `configBucket`Optional +##### `grantInvoke` ```typescript -public readonly configBucket: Bucket; +public grantInvoke(grantee: IGrantable): Grant ``` -- *Type:* aws-cdk-lib.aws_s3.Bucket - ---- - +Grant the given identity permissions to invoke this Lambda. -### NextJsAssetsDeployment +###### `grantee`Required -Uploads NextJS-built static and public files to S3. +- *Type:* aws-cdk-lib.aws_iam.IGrantable -Will rewrite CloudFormation references with their resolved values after uploading. +--- -#### Initializers +##### `grantInvokeUrl` ```typescript -import { NextJsAssetsDeployment } from 'cdk-nextjs-standalone' - -new NextJsAssetsDeployment(scope: Construct, id: string, props: NextjsAssetsDeploymentProps) +public grantInvokeUrl(grantee: IGrantable): Grant ``` -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | NextjsAssetsDeploymentProps | *No description.* | - ---- +Grant the given identity permissions to invoke this Lambda Function URL. -##### `scope`Required +###### `grantee`Required -- *Type:* constructs.Construct +- *Type:* aws-cdk-lib.aws_iam.IGrantable --- -##### `id`Required +##### `metric` -- *Type:* string +```typescript +public metric(metricName: string, props?: MetricOptions): Metric +``` ---- +Return the given named metric for this Function. -##### `props`Required +###### `metricName`Required -- *Type:* NextjsAssetsDeploymentProps +- *Type:* string --- -#### Methods +###### `props`Optional -| **Name** | **Description** | -| --- | --- | -| toString | Returns a string representation of this construct. | +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `toString` +##### `metricDuration` ```typescript -public toString(): string +public metricDuration(props?: MetricOptions): Metric ``` -Returns a string representation of this construct. +How long execution of this Lambda takes. -#### Static Functions +Average over 5 minutes -| **Name** | **Description** | -| --- | --- | -| isConstruct | Checks if `x` is a construct. | +###### `props`Optional + +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### ~~`isConstruct`~~ +##### `metricErrors` ```typescript -import { NextJsAssetsDeployment } from 'cdk-nextjs-standalone' - -NextJsAssetsDeployment.isConstruct(x: any) +public metricErrors(props?: MetricOptions): Metric ``` -Checks if `x` is a construct. - -###### `x`Required - -- *Type:* any - -Any object. +How many invocations of this Lambda fail. ---- +Sum over 5 minutes -#### Properties +###### `props`Optional -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| node | constructs.Node | The tree node. | -| bucket | aws-cdk-lib.aws_s3.IBucket | Bucket containing assets. | -| deployments | aws-cdk-lib.aws_s3_deployment.BucketDeployment[] | Asset deployments to S3. | -| staticTempDir | string | *No description.* | -| rewriter | NextjsS3EnvRewriter | *No description.* | +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `node`Required +##### `metricInvocations` ```typescript -public readonly node: Node; +public metricInvocations(props?: MetricOptions): Metric ``` -- *Type:* constructs.Node +How often this Lambda is invoked. -The tree node. +Sum over 5 minutes + +###### `props`Optional + +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `bucket`Required +##### `metricThrottles` ```typescript -public readonly bucket: IBucket; +public metricThrottles(props?: MetricOptions): Metric ``` -- *Type:* aws-cdk-lib.aws_s3.IBucket +How often this Lambda is throttled. -Bucket containing assets. +Sum over 5 minutes + +###### `props`Optional + +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `deployments`Required +##### `addAlias` ```typescript -public readonly deployments: BucketDeployment[]; +public addAlias(aliasName: string, options?: AliasOptions): Alias ``` -- *Type:* aws-cdk-lib.aws_s3_deployment.BucketDeployment[] +Defines an alias for this function. -Asset deployments to S3. +The alias will automatically be updated to point to the latest version of +the function as it is being updated during a deployment. ---- +```ts +declare const fn: lambda.Function; -##### `staticTempDir`Required +fn.addAlias('Live'); -```typescript -public readonly staticTempDir: string; +// Is equivalent to + +new lambda.Alias(this, 'AliasLive', { + aliasName: 'Live', + version: fn.currentVersion, +}); ``` +###### `aliasName`Required + - *Type:* string +The name of the alias. + --- -##### `rewriter`Optional +###### `options`Optional -```typescript -public readonly rewriter: NextjsS3EnvRewriter; -``` +- *Type:* aws-cdk-lib.aws_lambda.AliasOptions -- *Type:* NextjsS3EnvRewriter +Alias options. --- +##### `addEnvironment` -### NextjsBuild - -Represents a built NextJS application. +```typescript +public addEnvironment(key: string, value: string, options?: EnvironmentOptions): Function +``` -This construct runs `npm build` in standalone output mode inside your `nextjsPath`. -This construct can be used by higher level constructs or used directly. +Adds an environment variable to this Lambda function. -#### Initializers +If this is a ref to a Lambda function, this operation results in a no-op. -```typescript -import { NextjsBuild } from 'cdk-nextjs-standalone' +###### `key`Required -new NextjsBuild(scope: Construct, id: string, props: NextjsBuildProps) -``` +- *Type:* string -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | NextjsBuildProps | *No description.* | +The environment variable key. --- -##### `scope`Required +###### `value`Required -- *Type:* constructs.Construct +- *Type:* string + +The environment variable's value. --- -##### `id`Required +###### `options`Optional + +- *Type:* aws-cdk-lib.aws_lambda.EnvironmentOptions -- *Type:* string +Environment variable options. --- -##### `props`Required +##### `addLayers` -- *Type:* NextjsBuildProps +```typescript +public addLayers(layers: ILayerVersion): void +``` ---- +Adds one or more Lambda Layers to this Lambda function. -#### Methods +###### `layers`Required -| **Name** | **Description** | -| --- | --- | -| toString | Returns a string representation of this construct. | -| readPublicFileList | *No description.* | +- *Type:* aws-cdk-lib.aws_lambda.ILayerVersion + +the layers to be added. --- -##### `toString` +##### `invalidateVersionBasedOn` ```typescript -public toString(): string +public invalidateVersionBasedOn(x: string): void ``` -Returns a string representation of this construct. +Mix additional information into the hash of the Version object. -##### `readPublicFileList` +The Lambda Function construct does its best to automatically create a new +Version when anything about the Function changes (its code, its layers, +any of the other properties). -```typescript -public readPublicFileList(): string[] -``` +However, you can sometimes source information from places that the CDK cannot +look into, like the deploy-time values of SSM parameters. In those cases, +the CDK would not force the creation of a new Version object when it actually +should. + +This method can be used to invalidate the current Version object. Pass in +any string into this method, and make sure the string changes when you know +a new Version needs to be created. + +This method may be called more than once. + +###### `x`Required + +- *Type:* string + +--- #### Static Functions | **Name** | **Description** | | --- | --- | -| isConstruct | Checks if `x` is a construct. | +| isConstruct | Checks if `x` is a construct. | +| isOwnedResource | Returns true if the construct was created by CDK, and false otherwise. | +| isResource | Check whether the given construct is a Resource. | +| classifyVersionProperty | Record whether specific properties in the `AWS::Lambda::Function` resource should also be associated to the Version resource. | +| fromFunctionArn | Import a lambda function into the CDK using its ARN. | +| fromFunctionAttributes | Creates a Lambda function object which represents a function not defined within this stack. | +| fromFunctionName | Import a lambda function into the CDK using its name. | +| metricAll | Return the given named metric for this Lambda. | +| metricAllConcurrentExecutions | Metric for the number of concurrent executions across all Lambdas. | +| metricAllDuration | Metric for the Duration executing all Lambdas. | +| metricAllErrors | Metric for the number of Errors executing all Lambdas. | +| metricAllInvocations | Metric for the number of invocations of all Lambdas. | +| metricAllThrottles | Metric for the number of throttled invocations of all Lambdas. | +| metricAllUnreservedConcurrentExecutions | Metric for the number of unreserved concurrent executions across all Lambdas. | --- -##### ~~`isConstruct`~~ +##### ~~`isConstruct`~~ ```typescript -import { NextjsBuild } from 'cdk-nextjs-standalone' +import { NextjsImage } from 'cdk-nextjs-standalone' -NextjsBuild.isConstruct(x: any) +NextjsImage.isConstruct(x: any) ``` Checks if `x` is a construct. -###### `x`Required +###### `x`Required - *Type:* any @@ -1697,538 +1516,564 @@ Any object. --- -#### Properties - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| node | constructs.Node | The tree node. | -| nextCacheDir | string | Cache directory for generated data. | -| nextImageFnDir | string | Contains function for processessing image requests. | -| nextRevalidateFnDir | string | Contains function for processing items from revalidation queue. | -| nextServerFnDir | string | Contains server code and dependencies. | -| nextStaticDir | string | Static files containing client-side code. | -| projectRoot | string | *No description.* | -| props | NextjsBuildProps | *No description.* | -| nextMiddlewareFnDir | string | Contains code for middleware. | - ---- - -##### `node`Required +##### `isOwnedResource` ```typescript -public readonly node: Node; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.isOwnedResource(construct: IConstruct) ``` -- *Type:* constructs.Node +Returns true if the construct was created by CDK, and false otherwise. -The tree node. +###### `construct`Required + +- *Type:* constructs.IConstruct --- -##### `nextCacheDir`Required +##### `isResource` ```typescript -public readonly nextCacheDir: string; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.isResource(construct: IConstruct) ``` -- *Type:* string +Check whether the given construct is a Resource. -Cache directory for generated data. +###### `construct`Required + +- *Type:* constructs.IConstruct --- -##### `nextImageFnDir`Required +##### `classifyVersionProperty` ```typescript -public readonly nextImageFnDir: string; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.classifyVersionProperty(propertyName: string, locked: boolean) ``` -- *Type:* string +Record whether specific properties in the `AWS::Lambda::Function` resource should also be associated to the Version resource. -Contains function for processessing image requests. +See 'currentVersion' section in the module README for more details. -Should be arm64. +###### `propertyName`Required ---- +- *Type:* string -##### `nextRevalidateFnDir`Required +The property to classify. -```typescript -public readonly nextRevalidateFnDir: string; -``` +--- -- *Type:* string +###### `locked`Required -Contains function for processing items from revalidation queue. +- *Type:* boolean + +whether the property should be associated to the version or not. --- -##### `nextServerFnDir`Required +##### `fromFunctionArn` ```typescript -public readonly nextServerFnDir: string; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.fromFunctionArn(scope: Construct, id: string, functionArn: string) ``` -- *Type:* string +Import a lambda function into the CDK using its ARN. -Contains server code and dependencies. +###### `scope`Required ---- +- *Type:* constructs.Construct -##### `nextStaticDir`Required +--- -```typescript -public readonly nextStaticDir: string; -``` +###### `id`Required - *Type:* string -Static files containing client-side code. - --- -##### `projectRoot`Required - -```typescript -public readonly projectRoot: string; -``` +###### `functionArn`Required - *Type:* string --- -##### `props`Required +##### `fromFunctionAttributes` ```typescript -public readonly props: NextjsBuildProps; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.fromFunctionAttributes(scope: Construct, id: string, attrs: FunctionAttributes) ``` -- *Type:* NextjsBuildProps +Creates a Lambda function object which represents a function not defined within this stack. ---- +###### `scope`Required -##### `nextMiddlewareFnDir`Optional +- *Type:* constructs.Construct -```typescript -public readonly nextMiddlewareFnDir: string; -``` +The parent construct. -- *Type:* string +--- -Contains code for middleware. +###### `id`Required -Not currently used. +- *Type:* string + +The name of the lambda construct. --- +###### `attrs`Required -### NextjsDistribution +- *Type:* aws-cdk-lib.aws_lambda.FunctionAttributes -Create a CloudFront distribution to serve a Next.js application. +the attributes of the function to import. -#### Initializers +--- + +##### `fromFunctionName` ```typescript -import { NextjsDistribution } from 'cdk-nextjs-standalone' +import { NextjsImage } from 'cdk-nextjs-standalone' -new NextjsDistribution(scope: Construct, id: string, props: NextjsDistributionProps) +NextjsImage.fromFunctionName(scope: Construct, id: string, functionName: string) ``` -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | NextjsDistributionProps | *No description.* | +Import a lambda function into the CDK using its name. + +###### `scope`Required + +- *Type:* constructs.Construct --- -##### `scope`Required +###### `id`Required -- *Type:* constructs.Construct +- *Type:* string --- -##### `id`Required +###### `functionName`Required - *Type:* string --- -##### `props`Required +##### `metricAll` -- *Type:* NextjsDistributionProps +```typescript +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.metricAll(metricName: string, props?: MetricOptions) +``` + +Return the given named metric for this Lambda. + +###### `metricName`Required + +- *Type:* string --- -#### Methods +###### `props`Optional -| **Name** | **Description** | -| --- | --- | -| toString | Returns a string representation of this construct. | +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `toString` +##### `metricAllConcurrentExecutions` ```typescript -public toString(): string +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.metricAllConcurrentExecutions(props?: MetricOptions) ``` -Returns a string representation of this construct. +Metric for the number of concurrent executions across all Lambdas. -#### Static Functions +###### `props`Optional -| **Name** | **Description** | -| --- | --- | -| isConstruct | Checks if `x` is a construct. | +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### ~~`isConstruct`~~ +##### `metricAllDuration` ```typescript -import { NextjsDistribution } from 'cdk-nextjs-standalone' +import { NextjsImage } from 'cdk-nextjs-standalone' -NextjsDistribution.isConstruct(x: any) +NextjsImage.metricAllDuration(props?: MetricOptions) ``` -Checks if `x` is a construct. - -###### `x`Required +Metric for the Duration executing all Lambdas. -- *Type:* any +###### `props`Optional -Any object. +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -#### Properties +##### `metricAllErrors` -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| node | constructs.Node | The tree node. | -| fallbackOriginRequestPolicyProps | aws-cdk-lib.aws_cloudfront.OriginRequestPolicyProps | *No description.* | -| imageCachePolicyProps | aws-cdk-lib.aws_cloudfront.CachePolicyProps | The default CloudFront cache policy properties for images. | -| imageOptimizationOriginRequestPolicyProps | aws-cdk-lib.aws_cloudfront.OriginRequestPolicyProps | *No description.* | -| lambdaCachePolicyProps | aws-cdk-lib.aws_cloudfront.CachePolicyProps | The default CloudFront cache policy properties for the Lambda server handler. | -| lambdaOriginRequestPolicyProps | aws-cdk-lib.aws_cloudfront.OriginRequestPolicyProps | The default CloudFront lambda origin request policy. | -| staticCachePolicyProps | aws-cdk-lib.aws_cloudfront.CachePolicyProps | The default CloudFront cache policy properties for static pages. | -| distributionDomain | string | The domain name of the internally created CloudFront Distribution. | -| distributionId | string | The ID of the internally created CloudFront Distribution. | -| url | string | The CloudFront URL of the website. | -| customDomainName | string | *No description.* | -| customDomainUrl | string | If the custom domain is enabled, this is the URL of the website with the custom domain. | -| distribution | aws-cdk-lib.aws_cloudfront.Distribution | The internally created CloudFront `Distribution` instance. | -| tempBuildDir | string | *No description.* | -| certificate | aws-cdk-lib.aws_certificatemanager.ICertificate | The AWS Certificate Manager certificate for the custom domain. | -| hostedZone | aws-cdk-lib.aws_route53.IHostedZone | The Route 53 hosted zone for the custom domain. | +```typescript +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.metricAllErrors(props?: MetricOptions) +``` + +Metric for the number of Errors executing all Lambdas. + +###### `props`Optional + +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `node`Required +##### `metricAllInvocations` ```typescript -public readonly node: Node; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.metricAllInvocations(props?: MetricOptions) ``` -- *Type:* constructs.Node +Metric for the number of invocations of all Lambdas. -The tree node. +###### `props`Optional + +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `fallbackOriginRequestPolicyProps`Required +##### `metricAllThrottles` ```typescript -public readonly fallbackOriginRequestPolicyProps: OriginRequestPolicyProps; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.metricAllThrottles(props?: MetricOptions) ``` -- *Type:* aws-cdk-lib.aws_cloudfront.OriginRequestPolicyProps +Metric for the number of throttled invocations of all Lambdas. + +###### `props`Optional + +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `imageCachePolicyProps`Required +##### `metricAllUnreservedConcurrentExecutions` ```typescript -public readonly imageCachePolicyProps: CachePolicyProps; +import { NextjsImage } from 'cdk-nextjs-standalone' + +NextjsImage.metricAllUnreservedConcurrentExecutions(props?: MetricOptions) ``` -- *Type:* aws-cdk-lib.aws_cloudfront.CachePolicyProps +Metric for the number of unreserved concurrent executions across all Lambdas. + +###### `props`Optional -The default CloudFront cache policy properties for images. +- *Type:* aws-cdk-lib.aws_cloudwatch.MetricOptions --- -##### `imageOptimizationOriginRequestPolicyProps`Required +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| env | aws-cdk-lib.ResourceEnvironment | The environment this resource belongs to. | +| stack | aws-cdk-lib.Stack | The stack in which this resource is defined. | +| architecture | aws-cdk-lib.aws_lambda.Architecture | The architecture of this Lambda Function (this is an optional attribute and defaults to X86_64). | +| connections | aws-cdk-lib.aws_ec2.Connections | Access the Connections object. | +| functionArn | string | ARN of this function. | +| functionName | string | Name of this function. | +| grantPrincipal | aws-cdk-lib.aws_iam.IPrincipal | The principal this Lambda Function is running as. | +| isBoundToVpc | boolean | Whether or not this Lambda function was bound to a VPC. | +| latestVersion | aws-cdk-lib.aws_lambda.IVersion | The `$LATEST` version of this function. | +| permissionsNode | constructs.Node | The construct node where permissions are attached. | +| resourceArnsForGrantInvoke | string[] | The ARN(s) to put into the resource field of the generated IAM policy for grantInvoke(). | +| role | aws-cdk-lib.aws_iam.IRole | Execution role associated with this function. | +| currentVersion | aws-cdk-lib.aws_lambda.Version | Returns a `lambda.Version` which represents the current version of this Lambda function. A new version will be created every time the function's configuration changes. | +| logGroup | aws-cdk-lib.aws_logs.ILogGroup | The LogGroup where the Lambda function's logs are made available. | +| runtime | aws-cdk-lib.aws_lambda.Runtime | The runtime configured for this lambda. | +| deadLetterQueue | aws-cdk-lib.aws_sqs.IQueue | The DLQ (as queue) associated with this Lambda Function (this is an optional attribute). | +| deadLetterTopic | aws-cdk-lib.aws_sns.ITopic | The DLQ (as topic) associated with this Lambda Function (this is an optional attribute). | +| timeout | aws-cdk-lib.Duration | The timeout configured for this lambda. | +| bucket | aws-cdk-lib.aws_s3.IBucket | *No description.* | + +--- + +##### `node`Required ```typescript -public readonly imageOptimizationOriginRequestPolicyProps: OriginRequestPolicyProps; +public readonly node: Node; ``` -- *Type:* aws-cdk-lib.aws_cloudfront.OriginRequestPolicyProps +- *Type:* constructs.Node + +The tree node. --- -##### `lambdaCachePolicyProps`Required +##### `env`Required ```typescript -public readonly lambdaCachePolicyProps: CachePolicyProps; +public readonly env: ResourceEnvironment; ``` -- *Type:* aws-cdk-lib.aws_cloudfront.CachePolicyProps +- *Type:* aws-cdk-lib.ResourceEnvironment + +The environment this resource belongs to. -The default CloudFront cache policy properties for the Lambda server handler. +For resources that are created and managed by the CDK +(generally, those created by creating new class instances like Role, Bucket, etc.), +this is always the same as the environment of the stack they belong to; +however, for imported resources +(those obtained from static methods like fromRoleArn, fromBucketName, etc.), +that might be different than the stack they were imported into. --- -##### `lambdaOriginRequestPolicyProps`Required +##### `stack`Required ```typescript -public readonly lambdaOriginRequestPolicyProps: OriginRequestPolicyProps; +public readonly stack: Stack; ``` -- *Type:* aws-cdk-lib.aws_cloudfront.OriginRequestPolicyProps +- *Type:* aws-cdk-lib.Stack -The default CloudFront lambda origin request policy. +The stack in which this resource is defined. --- -##### `staticCachePolicyProps`Required +##### `architecture`Required ```typescript -public readonly staticCachePolicyProps: CachePolicyProps; +public readonly architecture: Architecture; ``` -- *Type:* aws-cdk-lib.aws_cloudfront.CachePolicyProps +- *Type:* aws-cdk-lib.aws_lambda.Architecture -The default CloudFront cache policy properties for static pages. +The architecture of this Lambda Function (this is an optional attribute and defaults to X86_64). --- -##### `distributionDomain`Required +##### `connections`Required ```typescript -public readonly distributionDomain: string; +public readonly connections: Connections; ``` -- *Type:* string +- *Type:* aws-cdk-lib.aws_ec2.Connections -The domain name of the internally created CloudFront Distribution. +Access the Connections object. + +Will fail if not a VPC-enabled Lambda Function --- -##### `distributionId`Required +##### `functionArn`Required ```typescript -public readonly distributionId: string; +public readonly functionArn: string; ``` - *Type:* string -The ID of the internally created CloudFront Distribution. +ARN of this function. --- -##### `url`Required +##### `functionName`Required ```typescript -public readonly url: string; +public readonly functionName: string; ``` - *Type:* string -The CloudFront URL of the website. +Name of this function. --- -##### `customDomainName`Optional +##### `grantPrincipal`Required ```typescript -public readonly customDomainName: string; +public readonly grantPrincipal: IPrincipal; ``` -- *Type:* string +- *Type:* aws-cdk-lib.aws_iam.IPrincipal + +The principal this Lambda Function is running as. --- -##### `customDomainUrl`Optional +##### `isBoundToVpc`Required ```typescript -public readonly customDomainUrl: string; +public readonly isBoundToVpc: boolean; ``` -- *Type:* string +- *Type:* boolean -If the custom domain is enabled, this is the URL of the website with the custom domain. +Whether or not this Lambda function was bound to a VPC. + +If this is is `false`, trying to access the `connections` object will fail. --- -##### `distribution`Required +##### `latestVersion`Required ```typescript -public readonly distribution: Distribution; +public readonly latestVersion: IVersion; ``` -- *Type:* aws-cdk-lib.aws_cloudfront.Distribution +- *Type:* aws-cdk-lib.aws_lambda.IVersion -The internally created CloudFront `Distribution` instance. +The `$LATEST` version of this function. + +Note that this is reference to a non-specific AWS Lambda version, which +means the function this version refers to can return different results in +different invocations. + +To obtain a reference to an explicit version which references the current +function configuration, use `lambdaFunction.currentVersion` instead. --- -##### `tempBuildDir`Required +##### `permissionsNode`Required ```typescript -public readonly tempBuildDir: string; +public readonly permissionsNode: Node; ``` -- *Type:* string +- *Type:* constructs.Node + +The construct node where permissions are attached. --- -##### `certificate`Optional +##### `resourceArnsForGrantInvoke`Required ```typescript -public readonly certificate: ICertificate; +public readonly resourceArnsForGrantInvoke: string[]; ``` -- *Type:* aws-cdk-lib.aws_certificatemanager.ICertificate +- *Type:* string[] -The AWS Certificate Manager certificate for the custom domain. +The ARN(s) to put into the resource field of the generated IAM policy for grantInvoke(). --- -##### `hostedZone`Optional +##### `role`Optional ```typescript -public readonly hostedZone: IHostedZone; +public readonly role: IRole; ``` -- *Type:* aws-cdk-lib.aws_route53.IHostedZone +- *Type:* aws-cdk-lib.aws_iam.IRole -The Route 53 hosted zone for the custom domain. +Execution role associated with this function. --- - -### NextJsLambda - -Build a lambda function from a NextJS application to handle server-side rendering, API routes, and image optimization. - -#### Initializers +##### `currentVersion`Required ```typescript -import { NextJsLambda } from 'cdk-nextjs-standalone' - -new NextJsLambda(scope: Construct, id: string, props: NextjsLambdaProps) +public readonly currentVersion: Version; ``` -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | NextjsLambdaProps | *No description.* | - ---- +- *Type:* aws-cdk-lib.aws_lambda.Version -##### `scope`Required +Returns a `lambda.Version` which represents the current version of this Lambda function. A new version will be created every time the function's configuration changes. -- *Type:* constructs.Construct +You can specify options for this version using the `currentVersionOptions` +prop when initializing the `lambda.Function`. --- -##### `id`Required - -- *Type:* string - ---- +##### `logGroup`Required -##### `props`Required +```typescript +public readonly logGroup: ILogGroup; +``` -- *Type:* NextjsLambdaProps +- *Type:* aws-cdk-lib.aws_logs.ILogGroup ---- +The LogGroup where the Lambda function's logs are made available. -#### Methods +If either `logRetention` is set or this property is called, a CloudFormation custom resource is added to the stack that +pre-creates the log group as part of the stack deployment, if it already doesn't exist, and sets the correct log retention +period (never expire, by default). -| **Name** | **Description** | -| --- | --- | -| toString | Returns a string representation of this construct. | +Further, if the log group already exists and the `logRetention` is not set, the custom resource will reset the log retention +to never expire even if it was configured with a different value. --- -##### `toString` +##### `runtime`Required ```typescript -public toString(): string +public readonly runtime: Runtime; ``` -Returns a string representation of this construct. - -#### Static Functions +- *Type:* aws-cdk-lib.aws_lambda.Runtime -| **Name** | **Description** | -| --- | --- | -| isConstruct | Checks if `x` is a construct. | +The runtime configured for this lambda. --- -##### ~~`isConstruct`~~ +##### `deadLetterQueue`Optional ```typescript -import { NextJsLambda } from 'cdk-nextjs-standalone' - -NextJsLambda.isConstruct(x: any) +public readonly deadLetterQueue: IQueue; ``` -Checks if `x` is a construct. - -###### `x`Required - -- *Type:* any - -Any object. - ---- - -#### Properties +- *Type:* aws-cdk-lib.aws_sqs.IQueue -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| node | constructs.Node | The tree node. | -| lambdaFunction | aws-cdk-lib.aws_lambda.Function | *No description.* | -| configBucket | aws-cdk-lib.aws_s3.Bucket | *No description.* | +The DLQ (as queue) associated with this Lambda Function (this is an optional attribute). --- -##### `node`Required +##### `deadLetterTopic`Optional ```typescript -public readonly node: Node; +public readonly deadLetterTopic: ITopic; ``` -- *Type:* constructs.Node +- *Type:* aws-cdk-lib.aws_sns.ITopic -The tree node. +The DLQ (as topic) associated with this Lambda Function (this is an optional attribute). --- -##### `lambdaFunction`Required +##### `timeout`Optional ```typescript -public readonly lambdaFunction: Function; +public readonly timeout: Duration; ``` -- *Type:* aws-cdk-lib.aws_lambda.Function +- *Type:* aws-cdk-lib.Duration + +The timeout configured for this lambda. --- -##### `configBucket`Optional +##### `bucket`Required ```typescript -public readonly configBucket: Bucket; +public readonly bucket: IBucket; ``` -- *Type:* aws-cdk-lib.aws_s3.Bucket +- *Type:* aws-cdk-lib.aws_s3.IBucket --- @@ -2604,21 +2449,125 @@ Returns a string representation of this construct. | **Name** | **Description** | | --- | --- | -| isConstruct | Checks if `x` is a construct. | +| isConstruct | Checks if `x` is a construct. | + +--- + +##### ~~`isConstruct`~~ + +```typescript +import { NextjsRevalidation } from 'cdk-nextjs-standalone' + +NextjsRevalidation.isConstruct(x: any) +``` + +Checks if `x` is a construct. + +###### `x`Required + +- *Type:* any + +Any object. + +--- + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | + +--- + +##### `node`Required + +```typescript +public readonly node: Node; +``` + +- *Type:* constructs.Node + +The tree node. + +--- + + +### NextjsS3EnvRewriter + +Rewrites variables in S3 objects after a deployment happens to replace CloudFormation tokens with their values. + +These values are not resolved at build time because they are +only known at deploy time. + +#### Initializers + +```typescript +import { NextjsS3EnvRewriter } from 'cdk-nextjs-standalone' + +new NextjsS3EnvRewriter(scope: Construct, id: string, props: NextjsS3EnvRewriterProps) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsS3EnvRewriterProps | *No description.* | + +--- + +##### `scope`Required + +- *Type:* constructs.Construct + +--- + +##### `id`Required + +- *Type:* string + +--- + +##### `props`Required + +- *Type:* NextjsS3EnvRewriterProps + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | + +--- + +##### `toString` + +```typescript +public toString(): string +``` + +Returns a string representation of this construct. + +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | --- -##### ~~`isConstruct`~~ +##### ~~`isConstruct`~~ ```typescript -import { NextjsRevalidation } from 'cdk-nextjs-standalone' +import { NextjsS3EnvRewriter } from 'cdk-nextjs-standalone' -NextjsRevalidation.isConstruct(x: any) +NextjsS3EnvRewriter.isConstruct(x: any) ``` Checks if `x` is a construct. -###### `x`Required +###### `x`Required - *Type:* any @@ -2630,11 +2579,12 @@ Any object. | **Name** | **Type** | **Description** | | --- | --- | --- | -| node | constructs.Node | The tree node. | +| node | constructs.Node | The tree node. | +| rewriteNode | constructs.Construct | *No description.* | --- -##### `node`Required +##### `node`Required ```typescript public readonly node: Node; @@ -2646,45 +2596,52 @@ The tree node. --- +##### `rewriteNode`Optional -### NextjsS3EnvRewriter +```typescript +public readonly rewriteNode: Construct; +``` -Rewrites variables in S3 objects after a deployment happens to replace CloudFormation tokens with their values. +- *Type:* constructs.Construct -These values are not resolved at build time because they are -only known at deploy time. +--- -#### Initializers + +### NextjsServer + +Build a lambda function from a NextJS application to handle server-side rendering, API routes, and image optimization. + +#### Initializers ```typescript -import { NextjsS3EnvRewriter } from 'cdk-nextjs-standalone' +import { NextjsServer } from 'cdk-nextjs-standalone' -new NextjsS3EnvRewriter(scope: Construct, id: string, props: NextjsS3EnvRewriterProps) +new NextjsServer(scope: Construct, id: string, props: NextjsServerProps) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | -| scope | constructs.Construct | *No description.* | -| id | string | *No description.* | -| props | NextjsS3EnvRewriterProps | *No description.* | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | NextjsServerProps | *No description.* | --- -##### `scope`Required +##### `scope`Required - *Type:* constructs.Construct --- -##### `id`Required +##### `id`Required - *Type:* string --- -##### `props`Required +##### `props`Required -- *Type:* NextjsS3EnvRewriterProps +- *Type:* NextjsServerProps --- @@ -2692,11 +2649,11 @@ new NextjsS3EnvRewriter(scope: Construct, id: string, props: NextjsS3EnvRewriter | **Name** | **Description** | | --- | --- | -| toString | Returns a string representation of this construct. | +| toString | Returns a string representation of this construct. | --- -##### `toString` +##### `toString` ```typescript public toString(): string @@ -2708,21 +2665,21 @@ Returns a string representation of this construct. | **Name** | **Description** | | --- | --- | -| isConstruct | Checks if `x` is a construct. | +| isConstruct | Checks if `x` is a construct. | --- -##### ~~`isConstruct`~~ +##### ~~`isConstruct`~~ ```typescript -import { NextjsS3EnvRewriter } from 'cdk-nextjs-standalone' +import { NextjsServer } from 'cdk-nextjs-standalone' -NextjsS3EnvRewriter.isConstruct(x: any) +NextjsServer.isConstruct(x: any) ``` Checks if `x` is a construct. -###### `x`Required +###### `x`Required - *Type:* any @@ -2734,12 +2691,13 @@ Any object. | **Name** | **Type** | **Description** | | --- | --- | --- | -| node | constructs.Node | The tree node. | -| rewriteNode | constructs.Construct | *No description.* | +| node | constructs.Node | The tree node. | +| lambdaFunction | aws-cdk-lib.aws_lambda.Function | *No description.* | +| configBucket | aws-cdk-lib.aws_s3.Bucket | *No description.* | --- -##### `node`Required +##### `node`Required ```typescript public readonly node: Node; @@ -2751,13 +2709,23 @@ The tree node. --- -##### `rewriteNode`Optional +##### `lambdaFunction`Required ```typescript -public readonly rewriteNode: Construct; +public readonly lambdaFunction: Function; ``` -- *Type:* constructs.Construct +- *Type:* aws-cdk-lib.aws_lambda.Function + +--- + +##### `configBucket`Optional + +```typescript +public readonly configBucket: Bucket; +``` + +- *Type:* aws-cdk-lib.aws_s3.Bucket --- @@ -3008,281 +2976,49 @@ public readonly directory: string; ```typescript public readonly zipFileName: string; -``` - -- *Type:* string - ---- - -##### `zipOutDir`Required - -```typescript -public readonly zipOutDir: string; -``` - -- *Type:* string - ---- - -##### `compressionLevel`Optional - -```typescript -public readonly compressionLevel: number; -``` - -- *Type:* number - ---- - -##### `fileGlob`Optional - -```typescript -public readonly fileGlob: string; -``` - -- *Type:* string - ---- - -##### `quiet`Optional - -```typescript -public readonly quiet: boolean; -``` - -- *Type:* boolean - ---- - -### ImageOptimizationProps - -#### Initializer - -```typescript -import { ImageOptimizationProps } from 'cdk-nextjs-standalone' - -const imageOptimizationProps: ImageOptimizationProps = { ... } -``` - -#### Properties - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| nextjsPath | string | Relative path to the directory where the NextJS project is located. | -| buildCommand | string | Optional value used to install NextJS node dependencies. | -| buildPath | string | The directory to execute `npm run build` from. | -| compressionLevel | number | 0 - no compression, fastest 9 - maximum compression, slowest. | -| environment | {[ key: string ]: string} | Custom environment variables to pass to the NextJS build and runtime. | -| isPlaceholder | boolean | Skip building app and deploy a placeholder. | -| nodeEnv | string | Optional value for NODE_ENV during build and runtime. | -| projectRoot | string | Root of your project, if different from `nextjsPath`. | -| quiet | boolean | Less build output. | -| sharpLayerArn | string | Optional arn for the sharp lambda layer. | -| skipFullInvalidation | boolean | By default all CloudFront cache will be invalidated on deployment. | -| tempBuildDir | string | Directory to store temporary build files in. | -| bucket | aws-cdk-lib.aws_s3.IBucket | The S3 bucket holding application images. | -| nextBuild | NextjsBuild | The `NextjsBuild` instance representing the built Nextjs application. | -| lambdaOptions | aws-cdk-lib.aws_lambda.FunctionOptions | Override function properties. | - ---- - -##### `nextjsPath`Required - -```typescript -public readonly nextjsPath: string; -``` - -- *Type:* string - -Relative path to the directory where the NextJS project is located. - -Can be the root of your project (`.`) or a subdirectory (`packages/web`). - ---- - -##### `buildCommand`Optional - -```typescript -public readonly buildCommand: string; -``` - -- *Type:* string - -Optional value used to install NextJS node dependencies. - -It defaults to 'npx --yes open-next@2 build' - ---- - -##### `buildPath`Optional - -```typescript -public readonly buildPath: string; -``` - -- *Type:* string - -The directory to execute `npm run build` from. - -By default, it is `nextjsPath`. -Can be overridden, particularly useful for monorepos where `build` is expected to run -at the root of the project. - ---- - -##### `compressionLevel`Optional - -```typescript -public readonly compressionLevel: number; -``` - -- *Type:* number -- *Default:* 1 - -0 - no compression, fastest 9 - maximum compression, slowest. - ---- - -##### `environment`Optional - -```typescript -public readonly environment: {[ key: string ]: string}; -``` - -- *Type:* {[ key: string ]: string} - -Custom environment variables to pass to the NextJS build and runtime. - ---- - -##### `isPlaceholder`Optional - -```typescript -public readonly isPlaceholder: boolean; -``` - -- *Type:* boolean - -Skip building app and deploy a placeholder. - -Useful when using `next dev` for local development. - ---- - -##### `nodeEnv`Optional - -```typescript -public readonly nodeEnv: string; -``` - -- *Type:* string - -Optional value for NODE_ENV during build and runtime. - ---- - -##### `projectRoot`Optional - -```typescript -public readonly projectRoot: string; -``` - -- *Type:* string - -Root of your project, if different from `nextjsPath`. - -Defaults to current working directory. - ---- - -##### `quiet`Optional - -```typescript -public readonly quiet: boolean; -``` - -- *Type:* boolean - -Less build output. - ---- - -##### `sharpLayerArn`Optional - -```typescript -public readonly sharpLayerArn: string; -``` - -- *Type:* string - -Optional arn for the sharp lambda layer. - -If omitted, the layer will be created. - ---- - -##### `skipFullInvalidation`Optional - -```typescript -public readonly skipFullInvalidation: boolean; -``` - -- *Type:* boolean - -By default all CloudFront cache will be invalidated on deployment. +``` -This can be set to true to skip the full cache invalidation, which -could be important for some users. +- *Type:* string --- -##### `tempBuildDir`Optional +##### `zipOutDir`Required ```typescript -public readonly tempBuildDir: string; +public readonly zipOutDir: string; ``` - *Type:* string -Directory to store temporary build files in. - -Defaults to os.tmpdir(). - --- -##### `bucket`Required +##### `compressionLevel`Optional ```typescript -public readonly bucket: IBucket; +public readonly compressionLevel: number; ``` -- *Type:* aws-cdk-lib.aws_s3.IBucket - -The S3 bucket holding application images. +- *Type:* number --- -##### `nextBuild`Required +##### `fileGlob`Optional ```typescript -public readonly nextBuild: NextjsBuild; +public readonly fileGlob: string; ``` -- *Type:* NextjsBuild - -The `NextjsBuild` instance representing the built Nextjs application. +- *Type:* string --- -##### `lambdaOptions`Optional +##### `quiet`Optional ```typescript -public readonly lambdaOptions: FunctionOptions; +public readonly quiet: boolean; ``` -- *Type:* aws-cdk-lib.aws_lambda.FunctionOptions - -Override function properties. +- *Type:* boolean --- @@ -4048,7 +3784,7 @@ const nextjsCachePolicyProps: NextjsCachePolicyProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | | imageCachePolicy | aws-cdk-lib.aws_cloudfront.ICachePolicy | *No description.* | -| lambdaCachePolicy | aws-cdk-lib.aws_cloudfront.ICachePolicy | *No description.* | +| serverCachePolicy | aws-cdk-lib.aws_cloudfront.ICachePolicy | *No description.* | | staticCachePolicy | aws-cdk-lib.aws_cloudfront.ICachePolicy | *No description.* | | staticClientMaxAgeDefault | aws-cdk-lib.Duration | Cache-control max-age default for static assets (/_next/*). | @@ -4064,10 +3800,10 @@ public readonly imageCachePolicy: ICachePolicy; --- -##### `lambdaCachePolicy`Optional +##### `serverCachePolicy`Optional ```typescript -public readonly lambdaCachePolicy: ICachePolicy; +public readonly serverCachePolicy: ICachePolicy; ``` - *Type:* aws-cdk-lib.aws_cloudfront.ICachePolicy @@ -4676,39 +4412,39 @@ Set this option if the domain is not hosted on Amazon Route 53. --- -### NextjsLambdaProps +### NextjsImageProps -#### Initializer +#### Initializer ```typescript -import { NextjsLambdaProps } from 'cdk-nextjs-standalone' +import { NextjsImageProps } from 'cdk-nextjs-standalone' -const nextjsLambdaProps: NextjsLambdaProps = { ... } +const nextjsImageProps: NextjsImageProps = { ... } ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| nextjsPath | string | Relative path to the directory where the NextJS project is located. | -| buildCommand | string | Optional value used to install NextJS node dependencies. | -| buildPath | string | The directory to execute `npm run build` from. | -| compressionLevel | number | 0 - no compression, fastest 9 - maximum compression, slowest. | -| environment | {[ key: string ]: string} | Custom environment variables to pass to the NextJS build and runtime. | -| isPlaceholder | boolean | Skip building app and deploy a placeholder. | -| nodeEnv | string | Optional value for NODE_ENV during build and runtime. | -| projectRoot | string | Root of your project, if different from `nextjsPath`. | -| quiet | boolean | Less build output. | -| sharpLayerArn | string | Optional arn for the sharp lambda layer. | -| skipFullInvalidation | boolean | By default all CloudFront cache will be invalidated on deployment. | -| tempBuildDir | string | Directory to store temporary build files in. | -| nextBuild | NextjsBuild | Built nextJS application. | -| staticAssetBucket | aws-cdk-lib.aws_s3.IBucket | Static asset bucket. | -| lambda | aws-cdk-lib.aws_lambda.FunctionOptions | Override function properties. | +| nextjsPath | string | Relative path to the directory where the NextJS project is located. | +| buildCommand | string | Optional value used to install NextJS node dependencies. | +| buildPath | string | The directory to execute `npm run build` from. | +| compressionLevel | number | 0 - no compression, fastest 9 - maximum compression, slowest. | +| environment | {[ key: string ]: string} | Custom environment variables to pass to the NextJS build and runtime. | +| isPlaceholder | boolean | Skip building app and deploy a placeholder. | +| nodeEnv | string | Optional value for NODE_ENV during build and runtime. | +| projectRoot | string | Root of your project, if different from `nextjsPath`. | +| quiet | boolean | Less build output. | +| sharpLayerArn | string | Optional arn for the sharp lambda layer. | +| skipFullInvalidation | boolean | By default all CloudFront cache will be invalidated on deployment. | +| tempBuildDir | string | Directory to store temporary build files in. | +| bucket | aws-cdk-lib.aws_s3.IBucket | The S3 bucket holding application images. | +| nextBuild | NextjsBuild | The `NextjsBuild` instance representing the built Nextjs application. | +| lambdaOptions | aws-cdk-lib.aws_lambda.FunctionOptions | Override function properties. | --- -##### `nextjsPath`Required +##### `nextjsPath`Required ```typescript public readonly nextjsPath: string; @@ -4722,7 +4458,7 @@ Can be the root of your project (`.`) or a subdirectory (`packages/web`). --- -##### `buildCommand`Optional +##### `buildCommand`Optional ```typescript public readonly buildCommand: string; @@ -4736,7 +4472,7 @@ It defaults to 'npx --yes open-next@2 build' --- -##### `buildPath`Optional +##### `buildPath`Optional ```typescript public readonly buildPath: string; @@ -4752,7 +4488,7 @@ at the root of the project. --- -##### `compressionLevel`Optional +##### `compressionLevel`Optional ```typescript public readonly compressionLevel: number; @@ -4765,7 +4501,7 @@ public readonly compressionLevel: number; --- -##### `environment`Optional +##### `environment`Optional ```typescript public readonly environment: {[ key: string ]: string}; @@ -4777,7 +4513,7 @@ Custom environment variables to pass to the NextJS build and runtime. --- -##### `isPlaceholder`Optional +##### `isPlaceholder`Optional ```typescript public readonly isPlaceholder: boolean; @@ -4791,7 +4527,7 @@ Useful when using `next dev` for local development. --- -##### `nodeEnv`Optional +##### `nodeEnv`Optional ```typescript public readonly nodeEnv: string; @@ -4803,7 +4539,7 @@ Optional value for NODE_ENV during build and runtime. --- -##### `projectRoot`Optional +##### `projectRoot`Optional ```typescript public readonly projectRoot: string; @@ -4817,7 +4553,7 @@ Defaults to current working directory. --- -##### `quiet`Optional +##### `quiet`Optional ```typescript public readonly quiet: boolean; @@ -4829,7 +4565,7 @@ Less build output. --- -##### `sharpLayerArn`Optional +##### `sharpLayerArn`Optional ```typescript public readonly sharpLayerArn: string; @@ -4843,7 +4579,7 @@ If omitted, the layer will be created. --- -##### `skipFullInvalidation`Optional +##### `skipFullInvalidation`Optional ```typescript public readonly skipFullInvalidation: boolean; @@ -4858,7 +4594,7 @@ could be important for some users. --- -##### `tempBuildDir`Optional +##### `tempBuildDir`Optional ```typescript public readonly tempBuildDir: string; @@ -4872,36 +4608,34 @@ Defaults to os.tmpdir(). --- -##### `nextBuild`Required +##### `bucket`Required ```typescript -public readonly nextBuild: NextjsBuild; +public readonly bucket: IBucket; ``` -- *Type:* NextjsBuild +- *Type:* aws-cdk-lib.aws_s3.IBucket -Built nextJS application. +The S3 bucket holding application images. --- -##### `staticAssetBucket`Required +##### `nextBuild`Required ```typescript -public readonly staticAssetBucket: IBucket; +public readonly nextBuild: NextjsBuild; ``` -- *Type:* aws-cdk-lib.aws_s3.IBucket - -Static asset bucket. +- *Type:* NextjsBuild -Function needs bucket to read from cache. +The `NextjsBuild` instance representing the built Nextjs application. --- -##### `lambda`Optional +##### `lambdaOptions`Optional ```typescript -public readonly lambda: FunctionOptions; +public readonly lambdaOptions: FunctionOptions; ``` - *Type:* aws-cdk-lib.aws_lambda.FunctionOptions @@ -4935,19 +4669,8 @@ const nextjsOriginRequestPolicyProps: NextjsOriginRequestPolicyProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | -| fallbackOriginRequestPolicy | aws-cdk-lib.aws_cloudfront.IOriginRequestPolicy | *No description.* | | imageOptimizationOriginRequestPolicy | aws-cdk-lib.aws_cloudfront.IOriginRequestPolicy | *No description.* | -| lambdaOriginRequestPolicy | aws-cdk-lib.aws_cloudfront.IOriginRequestPolicy | *No description.* | - ---- - -##### `fallbackOriginRequestPolicy`Optional - -```typescript -public readonly fallbackOriginRequestPolicy: IOriginRequestPolicy; -``` - -- *Type:* aws-cdk-lib.aws_cloudfront.IOriginRequestPolicy +| serverOriginRequestPolicy | aws-cdk-lib.aws_cloudfront.IOriginRequestPolicy | *No description.* | --- @@ -4961,10 +4684,10 @@ public readonly imageOptimizationOriginRequestPolicy: IOriginRequestPolicy; --- -##### `lambdaOriginRequestPolicy`Optional +##### `serverOriginRequestPolicy`Optional ```typescript -public readonly lambdaOriginRequestPolicy: IOriginRequestPolicy; +public readonly serverOriginRequestPolicy: IOriginRequestPolicy; ``` - *Type:* aws-cdk-lib.aws_cloudfront.IOriginRequestPolicy @@ -5438,6 +5161,240 @@ public readonly debug: boolean; --- +### NextjsServerProps + +#### Initializer + +```typescript +import { NextjsServerProps } from 'cdk-nextjs-standalone' + +const nextjsServerProps: NextjsServerProps = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| nextjsPath | string | Relative path to the directory where the NextJS project is located. | +| buildCommand | string | Optional value used to install NextJS node dependencies. | +| buildPath | string | The directory to execute `npm run build` from. | +| compressionLevel | number | 0 - no compression, fastest 9 - maximum compression, slowest. | +| environment | {[ key: string ]: string} | Custom environment variables to pass to the NextJS build and runtime. | +| isPlaceholder | boolean | Skip building app and deploy a placeholder. | +| nodeEnv | string | Optional value for NODE_ENV during build and runtime. | +| projectRoot | string | Root of your project, if different from `nextjsPath`. | +| quiet | boolean | Less build output. | +| sharpLayerArn | string | Optional arn for the sharp lambda layer. | +| skipFullInvalidation | boolean | By default all CloudFront cache will be invalidated on deployment. | +| tempBuildDir | string | Directory to store temporary build files in. | +| nextBuild | NextjsBuild | Built nextJS application. | +| staticAssetBucket | aws-cdk-lib.aws_s3.IBucket | Static asset bucket. | +| lambda | aws-cdk-lib.aws_lambda.FunctionOptions | Override function properties. | + +--- + +##### `nextjsPath`Required + +```typescript +public readonly nextjsPath: string; +``` + +- *Type:* string + +Relative path to the directory where the NextJS project is located. + +Can be the root of your project (`.`) or a subdirectory (`packages/web`). + +--- + +##### `buildCommand`Optional + +```typescript +public readonly buildCommand: string; +``` + +- *Type:* string + +Optional value used to install NextJS node dependencies. + +It defaults to 'npx --yes open-next@2 build' + +--- + +##### `buildPath`Optional + +```typescript +public readonly buildPath: string; +``` + +- *Type:* string + +The directory to execute `npm run build` from. + +By default, it is `nextjsPath`. +Can be overridden, particularly useful for monorepos where `build` is expected to run +at the root of the project. + +--- + +##### `compressionLevel`Optional + +```typescript +public readonly compressionLevel: number; +``` + +- *Type:* number +- *Default:* 1 + +0 - no compression, fastest 9 - maximum compression, slowest. + +--- + +##### `environment`Optional + +```typescript +public readonly environment: {[ key: string ]: string}; +``` + +- *Type:* {[ key: string ]: string} + +Custom environment variables to pass to the NextJS build and runtime. + +--- + +##### `isPlaceholder`Optional + +```typescript +public readonly isPlaceholder: boolean; +``` + +- *Type:* boolean + +Skip building app and deploy a placeholder. + +Useful when using `next dev` for local development. + +--- + +##### `nodeEnv`Optional + +```typescript +public readonly nodeEnv: string; +``` + +- *Type:* string + +Optional value for NODE_ENV during build and runtime. + +--- + +##### `projectRoot`Optional + +```typescript +public readonly projectRoot: string; +``` + +- *Type:* string + +Root of your project, if different from `nextjsPath`. + +Defaults to current working directory. + +--- + +##### `quiet`Optional + +```typescript +public readonly quiet: boolean; +``` + +- *Type:* boolean + +Less build output. + +--- + +##### `sharpLayerArn`Optional + +```typescript +public readonly sharpLayerArn: string; +``` + +- *Type:* string + +Optional arn for the sharp lambda layer. + +If omitted, the layer will be created. + +--- + +##### `skipFullInvalidation`Optional + +```typescript +public readonly skipFullInvalidation: boolean; +``` + +- *Type:* boolean + +By default all CloudFront cache will be invalidated on deployment. + +This can be set to true to skip the full cache invalidation, which +could be important for some users. + +--- + +##### `tempBuildDir`Optional + +```typescript +public readonly tempBuildDir: string; +``` + +- *Type:* string + +Directory to store temporary build files in. + +Defaults to os.tmpdir(). + +--- + +##### `nextBuild`Required + +```typescript +public readonly nextBuild: NextjsBuild; +``` + +- *Type:* NextjsBuild + +Built nextJS application. + +--- + +##### `staticAssetBucket`Required + +```typescript +public readonly staticAssetBucket: IBucket; +``` + +- *Type:* aws-cdk-lib.aws_s3.IBucket + +Static asset bucket. + +Function needs bucket to read from cache. + +--- + +##### `lambda`Optional + +```typescript +public readonly lambda: FunctionOptions; +``` + +- *Type:* aws-cdk-lib.aws_lambda.FunctionOptions + +Override function properties. + +--- + ### RevalidationProps #### Initializer @@ -5465,7 +5422,7 @@ const revalidationProps: RevalidationProps = { ... } | skipFullInvalidation | boolean | By default all CloudFront cache will be invalidated on deployment. | | tempBuildDir | string | Directory to store temporary build files in. | | nextBuild | NextjsBuild | The `NextjsBuild` instance representing the built Nextjs application. | -| serverFunction | NextJsLambda | The main NextJS server handler lambda function. | +| serverFunction | NextjsServer | The main NextJS server handler lambda function. | | lambdaOptions | aws-cdk-lib.aws_lambda.FunctionOptions | Override function properties. | --- @@ -5649,10 +5606,10 @@ The `NextjsBuild` instance representing the built Nextjs application. ##### `serverFunction`Required ```typescript -public readonly serverFunction: NextJsLambda; +public readonly serverFunction: NextjsServer; ``` -- *Type:* NextJsLambda +- *Type:* NextjsServer The main NextJS server handler lambda function. diff --git a/README.md b/README.md index 965c364e..9f19dc0b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ new Nextjs(this, 'Web', { }); ``` +## Important Notes +- Due to CloudFront's Distribution Cache Behavior pattern matching limitations, a cache behavior will be created for each top level file or directory in your `public/` folder. CloudFront has a soft limit of [25 cache behaviors per distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-web-distributions). Therefore, it's recommended to include all assets that can be under a top level folder like `public/static/`. Learn more in open-next docs [here](https://github.com/sst/open-next/blob/main/README.md#workaround-create-one-cache-behavior-per-top-level-file-and-folder-in-public-aws-specific). + ## Documentation Available on [Construct Hub](https://constructs.dev/packages/cdk-nextjs-standalone/). @@ -124,6 +127,19 @@ Don't manually update package.json or use npm CLI. Update dependencies in .proje ## Breaking changes +- v4.0.0 + - Renamed `NextjsLambda` to `NextjsServer` + - Renamed `ImageOptimizationLambda` to `NextjsImage` + - Renamed `NextjsCachePolicyProps.lambdaCachePolicy` to `NextjsCachePolicyProps.serverCachePolicy` + - Removed `NextjsOriginRequestPolicyProps.fallbackOriginRequestPolicy` + - Renamed `NextjsOriginRequestPolicyProps.lambdaOriginRequestPolicy` to `NextjsOriginRequestPolicyProps.serverOriginRequestPolicy` + - Removed `NextjsDistribution.staticCachePolicyProps` + - Renamed `NextjsDistribution.lambdaCachePolicyProps` to `NextjsDistribution.serverCachePolicyProps` + - Renamed `NextjsDistribution.lambdaOriginRequestPolicyProps` to `NextjsDistribution.serverOriginRequestPolicyProps` + - Removed `NextjsDistribution.fallbackOriginRequestPolicyProps` + - Removed `NextjsDistribution.imageOptimizationOriginRequestPolicyProps` + - NOTE: when upgrading to v4 from v3, the Lambda@Edge function will be renamed or removed. CloudFormation will fail to delete the function b/c they're replicated a take ~15 min to delete (more [here](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-delete-replicas.html)). You can either deploy CloudFormation with it's "no rollback" feature for a clean deployment or mark the Lambda@Edge function as "retain on delete". + - v3.0.0: Using open-next for building, ARM64 architecture for image handling, new build options. - v2.0.0: SST wrapper changed, lambda/assets/distribution defaults now are in the `defaults` prop, refactored distribution settings into the new NextjsDistribution construct. If you are upgrading, you must temporarily remove the `customDomain` on your existing 1.x.x app before upgrading to >=2.x.x because the CloudFront distribution will get recreated due to refactoring, and the custom domain must be globally unique across all CloudFront distributions. Prepare for downtime. diff --git a/assets/lambda@edge/LambdaOriginRequest.ts b/assets/lambda@edge/LambdaOriginRequest.ts deleted file mode 100644 index 0c953175..00000000 --- a/assets/lambda@edge/LambdaOriginRequest.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { CloudFrontRequestHandler } from 'aws-lambda'; -import { fixHostHeader, handleS3Request } from './common'; - -/** - * This fixes the "host" header to be the host of the origin. - * The origin is the lambda server function URL. - * If we don't provide its expected "host", it will not know how to route the request. - */ -export const handler: CloudFrontRequestHandler = async (event) => { - const request = event.Records[0].cf.request; - // console.log('request', JSON.stringify(request, null, 2)); - - handleS3Request(request); - fixHostHeader(request); - return request; -}; diff --git a/assets/lambda@edge/LambdaOriginRequestIamAuth.test.ts b/assets/lambda@edge/LambdaOriginRequestIamAuth.test.ts index 1efd3b7f..d5c96af1 100644 --- a/assets/lambda@edge/LambdaOriginRequestIamAuth.test.ts +++ b/assets/lambda@edge/LambdaOriginRequestIamAuth.test.ts @@ -1,11 +1,11 @@ import type { CloudFrontRequestEvent } from 'aws-lambda'; -import { getRegionFromLambdaUrl, isLambdaUrlRequest, signRequest } from './LambdaOriginRequestIamAuth'; +import { getRegionFromLambdaUrl, signRequest } from './LambdaOriginRequestIamAuth'; describe('LambdaOriginRequestIamAuth', () => { test('signRequest should add x-amz headers', async () => { // dummy AWS credentials process.env = { ...process.env, ...getFakeAwsCreds() }; - const event = getFakeCloudFrontLambdaUrlRequest(); + const event = getFakePageRequest(); const request = event.Records[0].cf.request; await signRequest(request); const securityHeaders = ['x-amz-date', 'x-amz-security-token', 'x-amz-content-sha256', 'authorization']; @@ -13,22 +13,15 @@ describe('LambdaOriginRequestIamAuth', () => { expect(hasSignedHeaders).toBe(true); }); - test('isLambdaFunctionUrl should correctly identity Lambda URL', () => { - const event = getFakeCloudFrontLambdaUrlRequest(); - const request = event.Records[0].cf.request; - const actual = isLambdaUrlRequest(request); - expect(actual).toBe(true); - }); - test('getRegionFromLambdaUrl should correctly get region', () => { - const event = getFakeCloudFrontLambdaUrlRequest(); + const event = getFakePageRequest(); const request = event.Records[0].cf.request; const actual = getRegionFromLambdaUrl(request.origin?.custom?.domainName || ''); expect(actual).toBe('us-east-1'); }); }); -function getFakeCloudFrontLambdaUrlRequest(): CloudFrontRequestEvent { +function getFakePageRequest(): CloudFrontRequestEvent { return { Records: [ { @@ -129,3 +122,83 @@ function getFakeAwsCreds() { 'ZQoJb3JpZ2luX2VjEFgaCXVzLWVhc3QtMSJGMEQCIHijzdTXh59aSe2hRfCWpFd2/jacPUC+8rCq3qBIiuG2AiAGX8jqld+p04nPYfuShi1lLN/Z1hEXG9QSNEmEFLTxGSqmAgiR//////////8BEAIaDDI2ODkxNDQ2NTIzMSIMrAMO5/GTvMgoG+chKvoB4f4V1TfkZiHOlmeMK6Ep58mav65A0WU3K9WPzdrJojnGqqTuS85zTlKhm3lfmMxCOtwS/OlOuiBQ1MZNlksK2je1FazgbXN46fNSi+iHiY9VfyRAd0wSLmXB8FFrCGsU92QOy/+deji0qIVadsjEyvBRxzQj5oIUI5sb74Yt7uNvka9fVZcT4s4IndYda0N7oZwIrApCuzzBMuoMAhabmgVrZTbiLmvOiFHS2XZWBySABdygqaIzfV7G4hjckvcXhtxpkw+HJUZTNzVUlspghzte1UG6VvIRV8ax3kWA3zqm8nA/1gHkl40DubJIXz1AJbg5Cps5moE1pjD7vNijBjqeAZh0Q/e0awIHnV4dXMfXUu5mWJ7Db9K1eUlSSL9FyiKeKd94HEdrbIrnPuIWVT/I/5RjNm7NgPYiqmpyx3fSpVcq9CKws0oEfBw6J9Hxk0IhV8yWFZYNMWIarUUZdmL9vVeJmFZmwyL4JjY1s/SZIU/oa8DtvkmP4RG4tTJfpyyhoKL0wJOevkYyoigNllBlLN59SZAT8CCADpN/B+sK', }; } + +function getFakeImageEvent(): CloudFrontRequestEvent { + return { + Records: [ + { + cf: { + config: { + distributionDomainName: 'd6b8brjqfujeb.cloudfront.net', + distributionId: 'EHX2SDUU61T7U', + eventType: 'origin-request', + requestId: '', + }, + request: { + body: { + action: 'read-only', + data: '', + encoding: 'base64', + inputTruncated: false, + }, + clientIp: '35.148.139.0', + headers: { + accept: [ + { + key: 'Accept', + value: + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', + }, + ], + 'x-forwarded-for': [ + { + key: 'X-Forwarded-For', + value: '35.148.139.0', + }, + ], + 'user-agent': [ + { + key: 'User-Agent', + value: 'Amazon CloudFront', + }, + ], + via: [ + { + key: 'Via', + value: '2.0 56233ac1c78ee7b920e664cc0c7f287e.cloudfront.net (CloudFront)', + }, + ], + 'accept-encoding': [ + { + key: 'Accept-Encoding', + value: 'br,gzip', + }, + ], + host: [ + { + key: 'Host', + value: 'lqlihcxizzcsefhpfcx2rnkgnu0pzrar.lambda-url.us-east-1.on.aws', + }, + ], + }, + method: 'GET', + origin: { + custom: { + customHeaders: {}, + domainName: 'lqlihcxizzcsefhpfcx2rnkgnu0pzrar.lambda-url.us-east-1.on.aws', + keepaliveTimeout: 5, + path: '', + port: 443, + protocol: 'https', + readTimeout: 30, + sslProtocols: ['TLSv1.2'], + }, + }, + querystring: 'url=%2Fprince-akachi-LWkFHEGpleE-unsplash.jpg&w=96&q=75&badParam=bad', + uri: '/_next/image', + }, + }, + }, + ], + }; +} diff --git a/assets/lambda@edge/LambdaOriginRequestIamAuth.ts b/assets/lambda@edge/LambdaOriginRequestIamAuth.ts index 6bda59fe..0d825dcb 100644 --- a/assets/lambda@edge/LambdaOriginRequestIamAuth.ts +++ b/assets/lambda@edge/LambdaOriginRequestIamAuth.ts @@ -1,8 +1,7 @@ -import qs from 'node:querystring'; +import qs, { escape } from 'node:querystring'; import { Sha256 } from '@aws-crypto/sha256-js'; import { SignatureV4 } from '@aws-sdk/signature-v4'; import type { CloudFrontHeaders, CloudFrontRequest, CloudFrontRequestHandler } from 'aws-lambda'; -import { fixHostHeader, handleS3Request } from './common'; const debug = false; @@ -13,23 +12,25 @@ const debug = false; */ export const handler: CloudFrontRequestHandler = async (event) => { const request = event.Records[0].cf.request; - if (debug) console.log('request', JSON.stringify(request, null, 2)); + if (debug) console.log('input request', JSON.stringify(request, null, 2)); - handleS3Request(request); - fixHostHeader(request); - if (isLambdaUrlRequest(request)) { - await signRequest(request); - } - if (debug) console.log(JSON.stringify(request), null, 2); + escapeQuerystring(request); + await signRequest(request); + + if (debug) console.log('output request', JSON.stringify(request), null, 2); return request; }; -let sigv4: SignatureV4; - -export function isLambdaUrlRequest(request: CloudFrontRequest) { - return /[a-z0-9]+\.lambda-url\.[a-z0-9-]+\.on\.aws/.test(request.origin?.custom?.domainName || ''); +/** + * Lambda URL will reject query parameters with brackets so we need to encode + * https://github.dev/pwrdrvr/lambda-url-signing/blob/main/packages/edge-to-origin/src/translate-request.ts#L19-L31 + */ +function escapeQuerystring(request: CloudFrontRequest) { + request.querystring = request.querystring.replace(/\[/g, '%5B').replace(/]/g, '%5D'); } +let sigv4: SignatureV4; + /** * When `NextjsDistributionProps.functionUrlAuthType` is set to * `lambda.FunctionUrlAuthType.AWS_IAM` we need to sign the `CloudFrontRequest`s @@ -44,12 +45,14 @@ export async function signRequest(request: CloudFrontRequest) { const region = getRegionFromLambdaUrl(request.origin?.custom?.domainName || ''); sigv4 = getSigV4(region); } - const headerBag = cfHeadersToHeaderBag(request); + // remove x-forwarded-for b/c it changes from hop to hop + delete request.headers['x-forwarded-for']; + const headerBag = cfHeadersToHeaderBag(request.headers); let body: string | undefined; if (request.body?.data) { body = Buffer.from(request.body.data, 'base64').toString(); } - const params = queryStringToParams(request); + const params = queryStringToQueryParamBag(request.querystring); const signed = await sigv4.sign({ method: request.method, headers: headerBag, @@ -88,7 +91,10 @@ export function getRegionFromLambdaUrl(url: string): string { return region; } -type HeaderBag = Record; +/** + * Bag or Map used for HeaderBag or QueryStringParameterBag for `sigv4.sign()` + */ +type Bag = Record; /** * Converts CloudFront headers (can have array of header values) to simple * header bag (object) required by `sigv4.sign` @@ -96,19 +102,13 @@ type HeaderBag = Record; * NOTE: only includes headers allowed by origin policy to prevent signature * mismatch */ -export function cfHeadersToHeaderBag(request: CloudFrontRequest): HeaderBag { - let headerBag: HeaderBag = {}; - for (const [header, values] of Object.entries(request.headers)) { - // don't sign 'x-forwarded-for' b/c it changes from hop to hop - if (header === 'x-forwarded-for') continue; - if (request.uri === '_next/image') { - // _next/image origin policy only allows accept - if (header === 'accept') { - headerBag[header] = values[0].value; - } - } else { - headerBag[header] = values[0].value; - } +export function cfHeadersToHeaderBag(headers: CloudFrontHeaders): Bag { + let headerBag: Bag = {}; + // assume first header value is the best match + // headerKey is case insensitive whereas key (adjacent property value that is + // not destructured) is case sensitive. we arbitrarily use case insensitive key + for (const [headerKey, [{ value }]] of Object.entries(headers)) { + headerBag[headerKey] = value; } return headerBag; } @@ -116,32 +116,26 @@ export function cfHeadersToHeaderBag(request: CloudFrontRequest): HeaderBag { /** * Converts simple header bag (object) to CloudFront headers */ -export function headerBagToCfHeaders(headerBag: HeaderBag): CloudFrontHeaders { +export function headerBagToCfHeaders(headerBag: Bag): CloudFrontHeaders { const cfHeaders: CloudFrontHeaders = {}; - for (const [header, value] of Object.entries(headerBag)) { - cfHeaders[header] = [{ key: header, value }]; + for (const [headerKey, value] of Object.entries(headerBag)) { + /* + When your Lambda function adds or modifies request headers and you don't include the header key field, Lambda@Edge automatically inserts a header key using the header name that you provide. Regardless of how you've formatted the header name, the header key that's inserted automatically is formatted with initial capitalization for each part, separated by hyphens (-). + See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html + */ + cfHeaders[headerKey] = [{ value }]; } return cfHeaders; } /** - * Converts CloudFront querystring to `HttpRequest.query` for IAM Sig V4 - * - * NOTE: only includes query parameters allowed at origin to prevent signature - * mismatch errors + * Converts CloudFront querystring to QueryParamaterBag for IAM Sig V4 */ -export function queryStringToParams(request: CloudFrontRequest) { - const params: Record = {}; - const _params = new URLSearchParams(request.querystring); - for (const [k, v] of _params) { - if (request.uri === '_next/image') { - // _next/image origin policy only allows these querystrings - if (['url', 'q', 'w'].includes(k)) { - params[k] = v; - } - } else { - params[k] = v; - } +export function queryStringToQueryParamBag(querystring: string): Bag { + const oldParams = new URLSearchParams(querystring); + const newParams: Bag = {}; + for (const [k, v] of oldParams) { + newParams[k] = v; } - return params; + return newParams; } diff --git a/assets/lambda@edge/common.ts b/assets/lambda@edge/common.ts deleted file mode 100644 index b3bcbc37..00000000 --- a/assets/lambda@edge/common.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { CloudFrontRequest } from 'aws-lambda'; - -export function handleS3Request(request: CloudFrontRequest) { - // remove cookies from requests to S3 - if (request.origin?.s3) { - request.headers.cookie = []; - request.headers.host = [ - { - key: 'host', - value: request.origin.s3.domainName, - }, - ]; // sending the wrong host header to S3 will cause a 403 - } -} - -/** - * This tries to fix the "host" header to be the host of the origin. The origin - * is the lambda server function URL. If we don't provide its expected "host", - * it will not know how to route the request. - */ -export function fixHostHeader(request: CloudFrontRequest) { - const originDomainName = request.origin?.custom?.domainName; - if (originDomainName) { - // fix host header and pass along the original host header - const originalHost = request.headers.host[0].value; - request.headers['x-forwarded-host'] = [{ key: 'x-forwarded-host', value: originalHost }]; - request.headers.host = [{ key: 'host', value: originDomainName }]; - } -} diff --git a/src/Nextjs.ts b/src/Nextjs.ts index 206f5c2d..cc447fe7 100644 --- a/src/Nextjs.ts +++ b/src/Nextjs.ts @@ -9,13 +9,13 @@ import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; import { Construct } from 'constructs'; import * as fs from 'fs-extra'; import { CACHE_BUCKET_KEY_PREFIX } from './constants'; -import { ImageOptimizationLambda } from './ImageOptimizationLambda'; import { NextJsAssetsDeployment, NextjsAssetsDeploymentProps } from './NextjsAssetsDeployment'; import { BaseSiteDomainProps, NextjsBaseProps } from './NextjsBase'; import { NextjsBuild } from './NextjsBuild'; import { NextjsDistribution, NextjsDistributionProps } from './NextjsDistribution'; -import { NextJsLambda } from './NextjsLambda'; +import { NextjsImage } from './NextjsImage'; import { NextjsRevalidation } from './NextjsRevalidation'; +import { NextjsServer } from './NextjsServer'; // contains server-side resolved environment vars in config bucket export const CONFIG_ENV_JSON_PATH = 'next-env.json'; @@ -80,12 +80,12 @@ export class Nextjs extends Construct { /** * The main NextJS server handler lambda function. */ - public serverFunction: NextJsLambda; + public serverFunction: NextjsServer; /** * The image optimization handler lambda function. */ - public imageOptimizationFunction: ImageOptimizationLambda; + public imageOptimizationFunction: NextjsImage; /** * Built NextJS project output. @@ -142,7 +142,7 @@ export class Nextjs extends Construct { // build nextjs app this.nextBuild = new NextjsBuild(this, id, { ...props, tempBuildDir }); - this.serverFunction = new NextJsLambda(this, 'ServerFn', { + this.serverFunction = new NextjsServer(this, 'ServerFn', { ...props, tempBuildDir, nextBuild: this.nextBuild, @@ -150,7 +150,7 @@ export class Nextjs extends Construct { staticAssetBucket: this.staticAssetBucket, }); // build image optimization - this.imageOptimizationFunction = new ImageOptimizationLambda(this, 'ImgOptFn', { + this.imageOptimizationFunction = new NextjsImage(this, 'ImgOptFn', { ...props, nextBuild: this.nextBuild, bucket: props.imageOptimizationBucket || this.bucket, diff --git a/src/NextjsBuild.ts b/src/NextjsBuild.ts index 5a107a2f..55361826 100644 --- a/src/NextjsBuild.ts +++ b/src/NextjsBuild.ts @@ -3,17 +3,18 @@ import { Token } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as spawn from 'cross-spawn'; import * as fs from 'fs-extra'; +import { + NEXTJS_BUILD_DIR, + NEXTJS_BUILD_IMAGE_FN_DIR, + NEXTJS_BUILD_MIDDLEWARE_FN_DIR, + NEXTJS_BUILD_REVALIDATE_FN_DIR, + NEXTJS_BUILD_SERVER_FN_DIR, + NEXTJS_CACHE_DIR, + NEXTJS_STATIC_DIR, +} from './constants'; import { listDirectory } from './NextjsAssetsDeployment'; import { CompressionLevel, NextjsBaseProps } from './NextjsBase'; -const NEXTJS_BUILD_DIR = '.open-next'; -const NEXTJS_STATIC_DIR = 'assets'; -const NEXTJS_CACHE_DIR = 'cache'; -const NEXTJS_BUILD_MIDDLEWARE_FN_DIR = 'middleware-function'; -const NEXTJS_BUILD_REVALIDATE_FN_DIR = 'revalidation-function'; -const NEXTJS_BUILD_IMAGE_FN_DIR = 'image-optimization-function'; -const NEXTJS_BUILD_SERVER_FN_DIR = 'server-function'; - export interface NextjsBuildProps extends NextjsBaseProps {} /** diff --git a/src/NextjsDistribution.ts b/src/NextjsDistribution.ts index 8d8669e3..1e17a7e1 100644 --- a/src/NextjsDistribution.ts +++ b/src/NextjsDistribution.ts @@ -16,7 +16,7 @@ import * as s3 from 'aws-cdk-lib/aws-s3'; import { Construct } from 'constructs'; import * as fs from 'fs-extra'; import { bundleFunction } from './BundleFunction'; -import { DEFAULT_STATIC_MAX_AGE } from './constants'; +import { DEFAULT_STATIC_MAX_AGE, NEXTJS_BUILD_DIR, NEXTJS_STATIC_DIR } from './constants'; import { BaseSiteDomainProps, buildErrorResponsesForRedirectToIndex, NextjsBaseProps } from './NextjsBase'; import { NextjsBuild } from './NextjsBuild'; @@ -37,7 +37,7 @@ export interface NextjsDistributionCdkProps { export interface NextjsCachePolicyProps { readonly staticCachePolicy?: cloudfront.ICachePolicy; - readonly lambdaCachePolicy?: cloudfront.ICachePolicy; + readonly serverCachePolicy?: cloudfront.ICachePolicy; readonly imageCachePolicy?: cloudfront.ICachePolicy; /** @@ -48,8 +48,7 @@ export interface NextjsCachePolicyProps { } export interface NextjsOriginRequestPolicyProps { - readonly lambdaOriginRequestPolicy?: cloudfront.IOriginRequestPolicy; - readonly fallbackOriginRequestPolicy?: cloudfront.IOriginRequestPolicy; + readonly serverOriginRequestPolicy?: cloudfront.IOriginRequestPolicy; readonly imageOptimizationOriginRequestPolicy?: cloudfront.IOriginRequestPolicy; } @@ -139,27 +138,33 @@ export interface NextjsDistributionProps extends NextjsBaseProps { */ export class NextjsDistribution extends Construct { /** - * The default CloudFront cache policy properties for static pages. + * The default CloudFront cache policy properties for dynamic requests to server handler. */ - public static staticCachePolicyProps: cloudfront.CachePolicyProps = { - queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(), - headerBehavior: cloudfront.CacheHeaderBehavior.none(), - cookieBehavior: cloudfront.CacheCookieBehavior.none(), - defaultTtl: Duration.days(30), - maxTtl: Duration.days(60), - minTtl: Duration.days(30), + public static serverCachePolicyProps: cloudfront.CachePolicyProps = { + queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(), + headerBehavior: cloudfront.CacheHeaderBehavior.allowList( + 'accept', + 'rsc', + 'next-router-prefetch', + 'next-router-state-tree', + 'next-url' + ), + cookieBehavior: cloudfront.CacheCookieBehavior.all(), + defaultTtl: Duration.seconds(0), + maxTtl: Duration.days(365), + minTtl: Duration.seconds(0), enableAcceptEncodingBrotli: true, enableAcceptEncodingGzip: true, - comment: 'Nextjs Static Default Cache Policy', + comment: 'Nextjs Server Default Cache Policy', }; /** - * The default CloudFront cache policy properties for images. + * The default CloudFront Cache Policy properties for images. */ public static imageCachePolicyProps: cloudfront.CachePolicyProps = { queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(), - headerBehavior: cloudfront.CacheHeaderBehavior.allowList('Accept'), - cookieBehavior: cloudfront.CacheCookieBehavior.none(), + headerBehavior: cloudfront.CacheHeaderBehavior.allowList('accept'), + cookieBehavior: cloudfront.CacheCookieBehavior.all(), defaultTtl: Duration.days(1), maxTtl: Duration.days(365), minTtl: Duration.days(0), @@ -168,49 +173,6 @@ export class NextjsDistribution extends Construct { comment: 'Nextjs Image Default Cache Policy', }; - /** - * The default CloudFront cache policy properties for the Lambda server handler. - */ - public static lambdaCachePolicyProps: cloudfront.CachePolicyProps = { - queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(), - headerBehavior: cloudfront.CacheHeaderBehavior.allowList('rsc', 'next-router-prefetch', 'next-router-state-tree'), - cookieBehavior: cloudfront.CacheCookieBehavior.all(), - defaultTtl: Duration.seconds(0), - maxTtl: Duration.days(365), - minTtl: Duration.seconds(0), - enableAcceptEncodingBrotli: true, - enableAcceptEncodingGzip: true, - comment: 'Nextjs Lambda Default Cache Policy', - }; - - /** - * The default CloudFront lambda origin request policy. - */ - public static lambdaOriginRequestPolicyProps: cloudfront.OriginRequestPolicyProps = { - cookieBehavior: cloudfront.OriginRequestCookieBehavior.all(), - queryStringBehavior: cloudfront.OriginRequestQueryStringBehavior.all(), - headerBehavior: cloudfront.OriginRequestHeaderBehavior.all(), // can't include host - comment: 'Nextjs Lambda Origin Request Policy', - }; - - public static fallbackOriginRequestPolicyProps: cloudfront.OriginRequestPolicyProps = { - cookieBehavior: cloudfront.OriginRequestCookieBehavior.all(), // pretty much disables caching - maybe can be changed - queryStringBehavior: cloudfront.OriginRequestQueryStringBehavior.all(), - headerBehavior: cloudfront.OriginRequestHeaderBehavior.all(), - comment: 'Nextjs Fallback Origin Request Policy', - }; - - public static imageOptimizationOriginRequestPolicyProps: cloudfront.OriginRequestPolicyProps = { - cookieBehavior: cloudfront.OriginRequestCookieBehavior.none(), - // NOTE: if `NextjsDistributionProps.functionUrlAuthType` is set to AWS_IAM - // auth, then the assets/lambda@edge/LambdaOriginRequestIamAuth.ts file - // needs to be updated to exclude these query strings/headers (below) from - // the signature calculation. Otherwise you'll get signature mismatch error. - queryStringBehavior: cloudfront.OriginRequestQueryStringBehavior.allowList('q', 'w', 'url'), - headerBehavior: cloudfront.OriginRequestHeaderBehavior.allowList('accept'), - comment: 'Nextjs Image Optimization Origin Request Policy', - }; - protected props: NextjsDistributionProps; ///////////////////// @@ -231,6 +193,21 @@ export class NextjsDistribution extends Construct { public tempBuildDir: string; + private commonBehaviorOptions: Pick = { + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + compress: true, + }; + + private s3Origin: origins.S3Origin; + + private staticBehaviorOptions: cloudfront.BehaviorOptions; + + private edgeLambdas: cloudfront.EdgeLambda[] = []; + + private serverBehaviorOptions: cloudfront.BehaviorOptions; + + private imageBehaviorOptions: cloudfront.BehaviorOptions; + constructor(scope: Construct, id: string, props: NextjsDistributionProps) { super(scope, id); @@ -249,10 +226,22 @@ export class NextjsDistribution extends Construct { this.hostedZone = this.lookupHostedZone(); this.certificate = this.createCertificate(); + // Create Behaviors + this.s3Origin = new origins.S3Origin(this.props.staticAssetsBucket); + this.staticBehaviorOptions = this.createStaticBehaviorOptions(); + if (this.props.functionUrlAuthType === lambda.FunctionUrlAuthType.AWS_IAM) { + this.edgeLambdas.push(this.createEdgeLambda()); + } + this.serverBehaviorOptions = this.createServerBehaviorOptions(); + this.imageBehaviorOptions = this.createImageBehaviorOptions(); + // Create CloudFront - this.distribution = this.props.isPlaceholder - ? this.createCloudFrontDistributionForStub() - : this.createCloudFrontDistribution(); + if (this.props.isPlaceholder) { + this.distribution = this.createCloudFrontDistributionForStub(); + } else { + this.distribution = this.createCloudFrontDistribution(); + this.addStaticBehaviorsToDistribution(); + } // Connect Custom Domain to CloudFront Distribution this.createRoute53Records(); @@ -306,93 +295,10 @@ export class NextjsDistribution extends Construct { return this.props.functionUrlAuthType === lambda.FunctionUrlAuthType.AWS_IAM; } - ///////////////////// - // CloudFront Distribution - ///////////////////// - - private createCloudFrontDistribution(): cloudfront.Distribution { - const { cdk: cdkProps, cachePolicies, originRequestPolicies } = this.props; - const cfDistributionProps = cdkProps?.distribution; - - // build domainNames - const domainNames = this.buildDistributionDomainNames(); - - // S3 origin - const s3Origin = new origins.S3Origin(this.props.staticAssetsBucket); - - const viewerProtocolPolicy = cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS; - - // handle placeholder - if (this.props.isPlaceholder) { - return new cloudfront.Distribution(this, 'Distribution', { - defaultRootObject: 'index.html', - errorResponses: buildErrorResponsesForRedirectToIndex('index.html'), - domainNames, - certificate: this.certificate, - defaultBehavior: { - origin: s3Origin, - viewerProtocolPolicy, - }, - }); - } - - // cache policies - const staticCachePolicy = cachePolicies?.staticCachePolicy ?? this.createCloudFrontStaticCachePolicy(); - const imageCachePolicy = cachePolicies?.imageCachePolicy ?? this.createCloudFrontImageCachePolicy(); - - // origin request policies - const lambdaOriginRequestPolicy = - originRequestPolicies?.lambdaOriginRequestPolicy ?? this.createLambdaOriginRequestPolicy(); - - const fnUrlAuthType: lambda.FunctionUrlAuthType = this.props.functionUrlAuthType || lambda.FunctionUrlAuthType.NONE; - // main server function origin (lambda URL HTTP origin) - const fnUrl = this.props.serverFunction.addFunctionUrl({ authType: fnUrlAuthType }); - const serverFunctionOrigin = new origins.HttpOrigin(Fn.parseDomainName(fnUrl.url)); - - // Image Optimization - const imageOptFnUrl = this.props.imageOptFunction.addFunctionUrl({ authType: fnUrlAuthType }); - const imageOptFunctionOrigin = new origins.HttpOrigin(Fn.parseDomainName(imageOptFnUrl.url)); - const imageOptORP = - originRequestPolicies?.imageOptimizationOriginRequestPolicy ?? this.createImageOptimizationOriginRequestPolicy(); - - // lambda behavior edge function - const lambdaOriginRequestEdgeFn = this.buildLambdaOriginRequestEdgeFunction(); - if (this.isFnUrlIamAuth) { - lambdaOriginRequestEdgeFn.addToRolePolicy( - new PolicyStatement({ - actions: ['lambda:InvokeFunctionUrl'], - resources: [this.props.serverFunction.functionArn, this.props.imageOptFunction.functionArn], - }) - ); - } - const lambdaOriginRequestEdgeFnVersion = lambda.Version.fromVersionArn( - this, - 'Version', - lambdaOriginRequestEdgeFn.currentVersion.functionArn - ); - const lambdaOriginEdgeFns: cloudfront.EdgeLambda[] = [ - { - eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, - functionVersion: lambdaOriginRequestEdgeFnVersion, - includeBody: this.isFnUrlIamAuth, - }, - ]; - - // default handler for requests that don't match any other path: - // - try S3 bucket first - // - if 403, 404, or 503 fall back to Lambda handler - // see discussion here: https://github.com/jetbridge/cdk-nextjs/pull/125#discussion_r1279212678 - const fallbackOriginGroup = new origins.OriginGroup({ - primaryOrigin: s3Origin, - fallbackOrigin: serverFunctionOrigin, - fallbackStatusCodes: [403, 404, 503], - }); - - const lambdaCachePolicy = cachePolicies?.lambdaCachePolicy ?? this.createCloudFrontLambdaCachePolicy(); - - // requests for static objects - const defaultStaticMaxAge = cachePolicies?.staticClientMaxAgeDefault?.toSeconds() || DEFAULT_STATIC_MAX_AGE; - const staticResponseHeadersPolicy = new ResponseHeadersPolicy(this, 'StaticResponseHeadersPolicy', { + private createStaticBehaviorOptions(): cloudfront.BehaviorOptions { + const staticClientMaxAge = this.props.cachePolicies?.staticClientMaxAgeDefault || DEFAULT_STATIC_MAX_AGE; + // TODO: remove this response headers policy once S3 files have correct cache control headers with new asset deployment technique + const responseHeadersPolicy = new ResponseHeadersPolicy(this, 'StaticResponseHeadersPolicy', { // add default header for static assets customHeadersBehavior: { customHeaders: [ @@ -401,55 +307,103 @@ export class NextjsDistribution extends Construct { override: false, // by default tell browser to cache static files for this long // this is separate from the origin cache policy - value: `public,max-age=${defaultStaticMaxAge},immutable`, + value: `public,max-age=${staticClientMaxAge},immutable`, }, ], }, }); - const staticBehavior: cloudfront.BehaviorOptions = { - viewerProtocolPolicy, - origin: s3Origin, + const cachePolicy = this.props.cachePolicies?.staticCachePolicy ?? cloudfront.CachePolicy.CACHING_OPTIMIZED; + return { + ...this.commonBehaviorOptions, + origin: this.s3Origin, allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS, - compress: true, - cachePolicy: staticCachePolicy, - responseHeadersPolicy: staticResponseHeadersPolicy, + cachePolicy, + responseHeadersPolicy, + }; + } + + private get fnUrlAuthType(): lambda.FunctionUrlAuthType { + return this.props.functionUrlAuthType || lambda.FunctionUrlAuthType.NONE; + } + + private createEdgeLambda(): cloudfront.EdgeLambda { + const originRequestEdgeFn = this.buildLambdaOriginRequestEdgeFunction(); + if (this.isFnUrlIamAuth) { + originRequestEdgeFn.addToRolePolicy( + new PolicyStatement({ + actions: ['lambda:InvokeFunctionUrl'], + resources: [this.props.serverFunction.functionArn, this.props.imageOptFunction.functionArn], + }) + ); + } + const originRequestEdgeFnVersion = lambda.Version.fromVersionArn( + this, + 'Version', + originRequestEdgeFn.currentVersion.functionArn + ); + return { + eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, + functionVersion: originRequestEdgeFnVersion, + includeBody: this.isFnUrlIamAuth, }; + } - // requests going to lambda (api, etc) - const lambdaBehavior: cloudfront.BehaviorOptions = { - viewerProtocolPolicy, - origin: serverFunctionOrigin, + private createServerBehaviorOptions(): cloudfront.BehaviorOptions { + const fnUrl = this.props.serverFunction.addFunctionUrl({ authType: this.fnUrlAuthType }); + const origin = new origins.HttpOrigin(Fn.parseDomainName(fnUrl.url)); + const originRequestPolicy = + this.props.originRequestPolicies?.serverOriginRequestPolicy ?? + cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER; + const cachePolicy = + this.props.cachePolicies?.serverCachePolicy ?? + new cloudfront.CachePolicy(this, 'ServerCachePolicy', NextjsDistribution.serverCachePolicyProps); + return { + ...this.commonBehaviorOptions, + origin, allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL, - // cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS, // this should be configurable - originRequestPolicy: lambdaOriginRequestPolicy, - compress: true, - cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED, - edgeLambdas: lambdaOriginEdgeFns, + originRequestPolicy, + cachePolicy, + edgeLambdas: this.edgeLambdas, }; + } - const imageBehavior: cloudfront.BehaviorOptions = { - viewerProtocolPolicy, - origin: imageOptFunctionOrigin, + private createImageBehaviorOptions(): cloudfront.BehaviorOptions { + const imageOptFnUrl = this.props.imageOptFunction.addFunctionUrl({ authType: this.fnUrlAuthType }); + const origin = new origins.HttpOrigin(Fn.parseDomainName(imageOptFnUrl.url)); + const originRequestPolicy = + this.props.originRequestPolicies?.imageOptimizationOriginRequestPolicy ?? + cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER; + const cachePolicy = + this.props.cachePolicies?.imageCachePolicy ?? + new cloudfront.CachePolicy(this, 'ImageCachePolicy', NextjsDistribution.imageCachePolicyProps); + return { + ...this.commonBehaviorOptions, + origin, allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS, cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS, - compress: true, - cachePolicy: imageCachePolicy, - originRequestPolicy: imageOptORP, - edgeLambdas: this.isFnUrlIamAuth ? lambdaOriginEdgeFns : [], + cachePolicy, + originRequestPolicy, + edgeLambdas: this.edgeLambdas, }; + } + + ///////////////////// + // CloudFront Distribution + ///////////////////// - // requests to fallback origin group (default behavior) - // used for S3 and lambda. would prefer to forward all headers to lambda but need to strip out host - // TODO: try to do this with headers whitelist or edge lambda - const fallbackOriginRequestPolicy = - originRequestPolicies?.fallbackOriginRequestPolicy ?? this.createFallbackOriginRequestPolicy(); + private createCloudFrontDistribution(): cloudfront.Distribution { + const { cdk: cdkProps } = this.props; + const cfDistributionProps = cdkProps?.distribution; + + // build domainNames + const domainNames = this.buildDistributionDomainNames(); // if we don't have a static file called index.html then we should // redirect to the lambda handler const hasIndexHtml = this.props.nextBuild.readPublicFileList().includes('index.html'); - return new cloudfront.Distribution(this, 'Distribution', { + const distribution = new cloudfront.Distribution(this, 'Distribution', { // defaultRootObject: "index.html", defaultRootObject: '', @@ -459,69 +413,41 @@ export class NextjsDistribution extends Construct { // these values can NOT be overwritten by cfDistributionProps domainNames, certificate: this.certificate, - defaultBehavior: { - origin: fallbackOriginGroup, // try S3 first, then lambda - viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - compress: true, - // what goes here? static or lambda? - cachePolicy: lambdaCachePolicy, - originRequestPolicy: fallbackOriginRequestPolicy, - edgeLambdas: lambdaOriginEdgeFns, - }, + defaultBehavior: this.serverBehaviorOptions, additionalBehaviors: { // is index.html static or dynamic? - ...(hasIndexHtml ? {} : { '/': lambdaBehavior }), + ...(hasIndexHtml ? {} : { '/': this.serverBehaviorOptions }), // known dynamic routes - 'api/*': lambdaBehavior, - '_next/data/*': lambdaBehavior, + 'api/*': this.serverBehaviorOptions, + '_next/data/*': this.serverBehaviorOptions, // dynamic images go to lambda - '_next/image*': imageBehavior, - - // known static routes - // it would be nice to create routes for all the static files we know of - // but we run into the limit of CacheBehaviors per distribution - '_next/*': staticBehavior, + '_next/image*': this.imageBehaviorOptions, }, }); + return distribution; } - private createCloudFrontStaticCachePolicy(): cloudfront.CachePolicy { - return new cloudfront.CachePolicy(this, 'StaticsCache', NextjsDistribution.staticCachePolicyProps); - } - - private createCloudFrontImageCachePolicy(): cloudfront.CachePolicy { - return new cloudfront.CachePolicy(this, 'ImageCache', NextjsDistribution.imageCachePolicyProps); - } - - private createLambdaOriginRequestPolicy(): cloudfront.OriginRequestPolicy { - return new cloudfront.OriginRequestPolicy( - this, - 'LambdaOriginPolicy', - NextjsDistribution.lambdaOriginRequestPolicyProps - ); - } - - private createFallbackOriginRequestPolicy(): cloudfront.OriginRequestPolicy { - return new cloudfront.OriginRequestPolicy( - this, - 'FallbackOriginRequestPolicy', - NextjsDistribution.fallbackOriginRequestPolicyProps - ); - } - - private createImageOptimizationOriginRequestPolicy(): cloudfront.OriginRequestPolicy { - return new cloudfront.OriginRequestPolicy( - this, - 'ImageOptPolicy', - NextjsDistribution.imageOptimizationOriginRequestPolicyProps - ); - } - - private createCloudFrontLambdaCachePolicy(): cloudfront.CachePolicy { - return new cloudfront.CachePolicy(this, 'LambdaCache', NextjsDistribution.lambdaCachePolicyProps); + private addStaticBehaviorsToDistribution() { + const publicFiles = fs.readdirSync(path.join(this.props.nextjsPath, NEXTJS_BUILD_DIR, NEXTJS_STATIC_DIR), { + withFileTypes: true, + }); + if (publicFiles.length >= 25) { + throw new Error( + `Too many public/ files in Next.js build. CloudFront limits Distributions to 25 Cache Behaviors. See documented limit here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-web-distributions` + ); + } + for (const publicFile of publicFiles) { + const pathPattern = publicFile.isDirectory() ? `${publicFile.name}/*` : publicFile.name; + if (!/^[a-zA-Z0-9_\-\.\*\$/~"'@:+?&]+$/.test(pathPattern)) { + throw new Error( + `Invalid CloudFront Distribution Cache Behavior Path Pattern: ${pathPattern}. Please see documentation here: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesPathPattern` + ); + } + this.distribution.addBehavior(pathPattern, this.s3Origin, this.staticBehaviorOptions); + } } private createCloudFrontDistributionForStub(): cloudfront.Distribution { @@ -531,7 +457,7 @@ export class NextjsDistribution extends Construct { domainNames: this.buildDistributionDomainNames(), certificate: this.certificate, defaultBehavior: { - origin: new origins.S3Origin(this.props.staticAssetsBucket), + origin: this.s3Origin, viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, }, ...this.props.cdk?.distribution, // not sure if needed @@ -552,19 +478,14 @@ export class NextjsDistribution extends Construct { * Create an edge function to handle requests to the lambda server handler origin. * It overrides the host header in the request to be the lambda URL's host. * It's needed because we forward all headers to the origin, but the origin is itself an - * HTTP server so it needs the host header to be the address of the lambda and not - * the distribution. - * + * HTTP server so it needs the host header to be the address of the lambda and not + * the distribution. */ private buildLambdaOriginRequestEdgeFunction() { const app = App.of(this) as App; // bundle the edge function - const fileName = - this.props.functionUrlAuthType === lambda.FunctionUrlAuthType.NONE - ? 'LambdaOriginRequest' - : 'LambdaOriginRequestIamAuth'; - const inputPath = path.join(__dirname, '..', 'assets', 'lambda@edge', fileName); + const inputPath = path.join(__dirname, '..', 'assets', 'lambda@edge', 'LambdaOriginRequestIamAuth'); const outputPath = path.join(this.tempBuildDir, 'lambda@edge', 'LambdaOriginRequest.js'); bundleFunction({ inputPath, @@ -578,7 +499,7 @@ export class NextjsDistribution extends Construct { }, }); - const fn = new cloudfront.experimental.EdgeFunction(this, 'DefaultOriginRequestEdgeFn', { + const fn = new cloudfront.experimental.EdgeFunction(this, 'EdgeFn', { runtime: Runtime.NODEJS_18_X, handler: 'LambdaOriginRequest.handler', code: lambda.Code.fromAsset(dirname(outputPath)), diff --git a/src/ImageOptimizationLambda.ts b/src/NextjsImage.ts similarity index 93% rename from src/ImageOptimizationLambda.ts rename to src/NextjsImage.ts index 8b400742..2ed37d64 100644 --- a/src/ImageOptimizationLambda.ts +++ b/src/NextjsImage.ts @@ -15,7 +15,7 @@ export type RemotePattern = { pathname?: string; }; -export interface ImageOptimizationProps extends NextjsBaseProps { +export interface NextjsImageProps extends NextjsBaseProps { /** * The S3 bucket holding application images. */ @@ -35,10 +35,10 @@ export interface ImageOptimizationProps extends NextjsBaseProps { /** * This lambda handles image optimization. */ -export class ImageOptimizationLambda extends Function { +export class NextjsImage extends Function { bucket: IBucket; - constructor(scope: Construct, id: string, props: ImageOptimizationProps) { + constructor(scope: Construct, id: string, props: NextjsImageProps) { const { lambdaOptions, bucket, isPlaceholder } = props; const code = isPlaceholder diff --git a/src/NextjsRevalidation.ts b/src/NextjsRevalidation.ts index 1e6c61dc..b9525faa 100644 --- a/src/NextjsRevalidation.ts +++ b/src/NextjsRevalidation.ts @@ -5,7 +5,7 @@ import { Queue } from 'aws-cdk-lib/aws-sqs'; import { Construct } from 'constructs'; import { NextjsBaseProps } from './NextjsBase'; import { NextjsBuild } from './NextjsBuild'; -import { NextJsLambda } from './NextjsLambda'; +import { NextjsServer } from './NextjsServer'; export interface RevalidationProps extends NextjsBaseProps { /** @@ -21,7 +21,7 @@ export interface RevalidationProps extends NextjsBaseProps { /** * The main NextJS server handler lambda function. */ - readonly serverFunction: NextJsLambda; + readonly serverFunction: NextjsServer; } /** diff --git a/src/NextjsLambda.ts b/src/NextjsServer.ts similarity index 96% rename from src/NextjsLambda.ts rename to src/NextjsServer.ts index b1167727..7d123505 100644 --- a/src/NextjsLambda.ts +++ b/src/NextjsServer.ts @@ -17,7 +17,7 @@ import { getS3ReplaceValues, NextjsS3EnvRewriter } from './NextjsS3EnvRewriter'; export type EnvironmentVars = Record; -function getEnvironment(props: NextjsLambdaProps): { [name: string]: string } { +function getEnvironment(props: NextjsServerProps): { [name: string]: string } { const environmentVariables: { [name: string]: string } = { ...props.environment, ...props.lambda?.environment, @@ -32,7 +32,7 @@ function getEnvironment(props: NextjsLambdaProps): { [name: string]: string } { return environmentVariables; } -export interface NextjsLambdaProps extends NextjsBaseProps { +export interface NextjsServerProps extends NextjsBaseProps { /** * Built nextJS application. */ @@ -52,11 +52,11 @@ export interface NextjsLambdaProps extends NextjsBaseProps { /** * Build a lambda function from a NextJS application to handle server-side rendering, API routes, and image optimization. */ -export class NextJsLambda extends Construct { +export class NextjsServer extends Construct { configBucket?: Bucket; lambdaFunction: Function; - constructor(scope: Construct, id: string, props: NextjsLambdaProps) { + constructor(scope: Construct, id: string, props: NextjsServerProps) { super(scope, id); const { nextBuild, lambda: functionOptions, isPlaceholder } = props; diff --git a/src/constants.ts b/src/constants.ts index f6be3ab9..c7a47499 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -13,3 +13,11 @@ export const LAMBDA_RUNTIME = Runtime.NODEJS_18_X; export const DEFAULT_LAMBA_MEMORY = 1536; export const CACHE_BUCKET_KEY_PREFIX = '_cache'; + +export const NEXTJS_STATIC_DIR = 'assets'; +export const NEXTJS_BUILD_DIR = '.open-next'; +export const NEXTJS_CACHE_DIR = 'cache'; +export const NEXTJS_BUILD_MIDDLEWARE_FN_DIR = 'middleware-function'; +export const NEXTJS_BUILD_REVALIDATE_FN_DIR = 'revalidation-function'; +export const NEXTJS_BUILD_IMAGE_FN_DIR = 'image-optimization-function'; +export const NEXTJS_BUILD_SERVER_FN_DIR = 'server-function'; diff --git a/src/index.ts b/src/index.ts index 3a3b8ec4..16b39886 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,8 +13,8 @@ export { } from './NextjsAssetsDeployment'; export { NextjsRevalidation, RevalidationProps } from './NextjsRevalidation'; export { NextjsBuild, NextjsBuildProps, CreateArchiveArgs } from './NextjsBuild'; -export { EnvironmentVars, NextJsLambda, NextjsLambdaProps } from './NextjsLambda'; -export { ImageOptimizationLambda, ImageOptimizationProps } from './ImageOptimizationLambda'; +export { EnvironmentVars, NextjsServer, NextjsServerProps } from './NextjsServer'; +export { NextjsImage, NextjsImageProps } from './NextjsImage'; export { NextjsS3EnvRewriter, NextjsS3EnvRewriterProps,