Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Commit c6402ed

Browse files
author
true
committed
feat(next): upgraded to 12.3 version, fixed broken implementation, dependencies simplified
1 parent d52cf06 commit c6402ed

14 files changed

+3486
-11125
lines changed

Diff for: .gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ nodejs/**
99
# Additional files that might existing for testing and dev purposes.
1010
.next/**
1111
next.out/**
12-
cdk/**
1312
cdk.out/**
13+
14+
cdk.json
15+
!cdk/cdk.json

Diff for: README.md

+13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This library uses Cloudfront, S3, ApiGateway and Lambdas to deploy easily in sec
1414
- [Server handler](#server-handler)
1515
- [Image handler](#image-handler)
1616
- [Via CDK](#via-cdk)
17+
- [Benchmark](#benchmark)
1718
- [Sharp layer](#sharp-layer)
1819
- [Next layer](#next-layer)
1920
- [Packaging](#packaging)
@@ -22,6 +23,7 @@ This library uses Cloudfront, S3, ApiGateway and Lambdas to deploy easily in sec
2223
- [Versioning](#versioning)
2324
- [Guess](#guess)
2425
- [Shipit](#shipit)
26+
- [TODO](#todo)
2527
- [Disclaimer](#disclaimer)
2628

2729
## TL;DR
@@ -91,6 +93,13 @@ This imports pre-made construct, you only need to worry about paths to outputed
9193

9294
> More granular CDK construct coming soon.
9395
96+
#### Benchmark
97+
98+
Creation of stack: 385sec (6min 25sec)
99+
Run #2 436sec (7min 16sec)
100+
Deletion of stack: 262sec (4min 22sec)
101+
Update of stack:
102+
94103

95104
### Sharp layer
96105

@@ -171,6 +180,10 @@ Similar to guess command, however, it automatically tags a commit on current bra
171180

172181
Simply call `@sladg/next-lambda shipit` on any branch and be done.
173182

183+
# TODO
184+
185+
- Move Next into peer dependency and build layer manually via npx. Parse next version from parent (allow parameter) to ensure compatibility with our implementation.
186+
- Move CDK into peer dependency and allow for CDK dependencies to not be used / to have version defined in upstream.
174187

175188
# Disclaimer
176189

Diff for: cdk/cdk.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"app": "npx ts-node --swc --project ../tsconfig.json ./example.ts",
3+
"context": {}
4+
}

Diff for: cdk/example.ts

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#!/usr/bin/env node
2+
import 'source-map-support/register'
3+
4+
import { HttpApi } from '@aws-cdk/aws-apigatewayv2-alpha'
5+
import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations-alpha'
6+
import { App, CfnOutput, Duration, RemovalPolicy, Stack, StackProps, SymlinkFollowMode } from 'aws-cdk-lib'
7+
import { CloudFrontAllowedMethods, CloudFrontWebDistribution, OriginAccessIdentity } from 'aws-cdk-lib/aws-cloudfront'
8+
import { Function } from 'aws-cdk-lib/aws-lambda'
9+
import { Code, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'
10+
import { Bucket } from 'aws-cdk-lib/aws-s3'
11+
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'
12+
13+
const app = new App()
14+
15+
class NextStandaloneStack extends Stack {
16+
constructor(scope: App, id: string, props?: StackProps) {
17+
super(scope, id, props)
18+
19+
const config = {
20+
assetsZipPath: './next.out/assetsLayer.zip',
21+
codeZipPath: './next.out/code.zip',
22+
dependenciesZipPath: './next.out/dependenciesLayer.zip',
23+
customServerHandler: 'handler.handler',
24+
customImageHandler: 'index.handler',
25+
cfnViewerCertificate: undefined,
26+
sharpLayerZipPath: './dist/sharp-layer.zip',
27+
nextLayerZipPath: './dist/next-layer.zip',
28+
imageHandlerZipPath: './dist/image-handler.zip',
29+
...props,
30+
}
31+
32+
const depsLayer = new LayerVersion(this, 'DepsLayer', {
33+
code: Code.fromAsset(config.dependenciesZipPath),
34+
})
35+
36+
const sharpLayer = new LayerVersion(this, 'SharpLayer', {
37+
code: Code.fromAsset(config.sharpLayerZipPath),
38+
})
39+
40+
const nextLayer = new LayerVersion(this, 'NextLayer', {
41+
code: Code.fromAsset(config.nextLayerZipPath),
42+
})
43+
44+
const serverLambda = new Function(this, 'DefaultNextJs', {
45+
code: Code.fromAsset(config.codeZipPath, {
46+
followSymlinks: SymlinkFollowMode.NEVER,
47+
}),
48+
runtime: Runtime.NODEJS_16_X,
49+
handler: config.customServerHandler,
50+
layers: [depsLayer, nextLayer],
51+
// No need for big memory as image handling is done elsewhere.
52+
memorySize: 512,
53+
timeout: Duration.seconds(15),
54+
})
55+
56+
const assetsBucket = new Bucket(this, 'NextAssetsBucket', {
57+
// Those settings are necessary for bucket to be removed on stack removal.
58+
removalPolicy: RemovalPolicy.DESTROY,
59+
autoDeleteObjects: true,
60+
publicReadAccess: false,
61+
})
62+
63+
const imageLambda = new Function(this, 'ImageOptimizationNextJs', {
64+
code: Code.fromAsset(config.imageHandlerZipPath),
65+
runtime: Runtime.NODEJS_16_X,
66+
handler: config.customImageHandler,
67+
layers: [sharpLayer, nextLayer],
68+
memorySize: 1024,
69+
timeout: Duration.seconds(10),
70+
environment: {
71+
S3_SOURCE_BUCKET: assetsBucket.bucketName,
72+
},
73+
})
74+
75+
assetsBucket.grantRead(imageLambda)
76+
77+
const serverApigatewayProxy = new HttpApi(this, 'ServerProxy', {
78+
createDefaultStage: true,
79+
defaultIntegration: new HttpLambdaIntegration('LambdaApigwIntegration', serverLambda),
80+
})
81+
82+
const imageApigatewayProxy = new HttpApi(this, 'ImagesProxy', {
83+
createDefaultStage: true,
84+
defaultIntegration: new HttpLambdaIntegration('ImagesApigwIntegration', imageLambda),
85+
})
86+
87+
const s3AssetsIdentity = new OriginAccessIdentity(this, 'OAICfnDistroS3', {
88+
comment: 'Allows CloudFront to access S3 bucket with assets',
89+
})
90+
91+
assetsBucket.grantRead(s3AssetsIdentity)
92+
93+
const cfnDistro = new CloudFrontWebDistribution(this, 'TestApigwDistro', {
94+
// Must be set, because cloufront would use index.html which would not match in NextJS routes.
95+
defaultRootObject: '',
96+
comment: 'ApiGwLambda Proxy for NextJS',
97+
viewerCertificate: config.cfnViewerCertificate,
98+
originConfigs: [
99+
{
100+
// Default behaviour, lambda handles.
101+
behaviors: [
102+
{
103+
allowedMethods: CloudFrontAllowedMethods.ALL,
104+
isDefaultBehavior: true,
105+
forwardedValues: { queryString: true },
106+
},
107+
{
108+
allowedMethods: CloudFrontAllowedMethods.ALL,
109+
pathPattern: '_next/data/*',
110+
},
111+
],
112+
customOriginSource: {
113+
domainName: `${serverApigatewayProxy.apiId}.execute-api.${this.region}.amazonaws.com`,
114+
},
115+
},
116+
{
117+
// Our implementation of image optimization, we are tapping into Next's default route to avoid need for next.config.js changes.
118+
behaviors: [
119+
{
120+
// Should use caching based on query params.
121+
allowedMethods: CloudFrontAllowedMethods.ALL,
122+
pathPattern: '_next/image*',
123+
forwardedValues: { queryString: true },
124+
},
125+
],
126+
customOriginSource: {
127+
domainName: `${imageApigatewayProxy.apiId}.execute-api.${this.region}.amazonaws.com`,
128+
},
129+
},
130+
{
131+
// Remaining next files (safe-catch) and our assets that are not imported via `next/image`
132+
behaviors: [
133+
{
134+
allowedMethods: CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
135+
pathPattern: '_next/*',
136+
},
137+
{
138+
allowedMethods: CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
139+
pathPattern: 'assets/*',
140+
},
141+
],
142+
s3OriginSource: {
143+
s3BucketSource: assetsBucket,
144+
originAccessIdentity: s3AssetsIdentity,
145+
},
146+
},
147+
],
148+
})
149+
150+
// This can be handled by `aws s3 sync` but we need to ensure invalidation of Cfn after deploy.
151+
new BucketDeployment(this, 'PublicFilesDeployment', {
152+
destinationBucket: assetsBucket,
153+
sources: [Source.asset('./next.out/assetsLayer.zip')],
154+
// Invalidate all paths after deployment.
155+
distribution: cfnDistro,
156+
distributionPaths: ['/*'],
157+
})
158+
159+
new CfnOutput(this, 'cfnDistroUrl', { value: cfnDistro.distributionDomainName })
160+
new CfnOutput(this, 'cfnDistroId', { value: cfnDistro.distributionId })
161+
new CfnOutput(this, 'defaultApiGwUrl', { value: serverApigatewayProxy.apiEndpoint })
162+
new CfnOutput(this, 'imagesApiGwUrl', { value: imageApigatewayProxy.apiEndpoint })
163+
new CfnOutput(this, 'assetsBucketUrl', { value: assetsBucket.bucketDomainName })
164+
}
165+
}
166+
167+
new NextStandaloneStack(app, 'StandaloneNextjsStack-Temporary')
168+
169+
app.synth()

Diff for: lib/cli.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { tmpdir } from 'os'
44
import path from 'path'
55
import { simpleGit } from 'simple-git'
66
import packageJson from '../package.json'
7+
import { skipCiFlag } from './consts'
78
import { bumpCalculator, bumpMapping, BumpType, findInFile, isValidTag, replaceVersionInCommonFiles, zipFolder, zipMultipleFoldersOrFiles } from './utils'
89

9-
const skipCiFlag = '[skip ci]'
1010
const commandCwd = process.cwd()
1111
const nextServerConfigRegex = /(?<=conf: )(.*)(?=,)/
1212
const scriptDir = path.dirname(__filename)
@@ -270,4 +270,11 @@ program
270270
console.log(`Successfuly tagged and created new branch - ${releaseBranch}`)
271271
})
272272

273+
program
274+
.command('deploy')
275+
.description('Deploy Next application via CDK')
276+
.action(async (options) => {
277+
// @TODO: Add support for CLI CDK deployments via provided CDK example.
278+
})
279+
273280
program.parse(process.argv)

0 commit comments

Comments
 (0)