Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: split up API Routes + use .nft.json files to make builds fast #2058

Merged
merged 75 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
418c46d
feat: split up API Routes
Skn0tt Apr 20, 2023
6e27836
feat: load includedFiles for every page
Skn0tt Apr 20, 2023
c09022c
refactor: extract function config logic
Skn0tt Apr 20, 2023
2ffd7a2
refactor: extract flag into own definition
Skn0tt Apr 20, 2023
0844815
feat: use "none" bundler for split-up api routes
Skn0tt Apr 20, 2023
60e71de
feat: list some more dependencies
Skn0tt Apr 20, 2023
caf4b69
feat: use NFT to trace common required files
Skn0tt Apr 21, 2023
d812eb6
refactor: clean up a wee bit
Skn0tt Apr 21, 2023
eb2abe8
fix: please don't include /sh
Skn0tt Apr 21, 2023
0f9cf0f
feat: enable flag by default, so tests use it
Skn0tt Apr 21, 2023
699ba69
feat: add a naïve packing algo
Skn0tt Apr 24, 2023
0a4fc56
feat: write rough sketch for packing lambdas
Skn0tt Apr 24, 2023
4cc2aff
refactor: add constructor for APILambda
Skn0tt Apr 24, 2023
12ebf75
feat: pack handlers together into bundles
Skn0tt Apr 25, 2023
3623916
fix: linter
Skn0tt Apr 25, 2023
45c3834
feat: exclude some heavy unneeded files
Skn0tt Apr 25, 2023
029dd98
Merge branch 'main' into split-api-routes
Skn0tt Apr 25, 2023
e5c4f23
fix: trigger CI again, now that it supports `none` bundler
Skn0tt Apr 25, 2023
57772f2
feat: remove code for old mechanism
Skn0tt Apr 25, 2023
567692d
fix: remove test for deleted code
Skn0tt Apr 25, 2023
7c0daa8
fix: ensure that react doesn't try to load development build
Skn0tt Apr 25, 2023
abec92a
fix: move test directory into repo, so node_modules can be read
Skn0tt Apr 26, 2023
63b8a73
fix: snapshot with API redirects
Skn0tt Apr 26, 2023
94de480
fix: remove .only
Skn0tt Apr 26, 2023
1d43d6b
Merge branch 'main' into split-api-routes
Skn0tt Apr 26, 2023
7af428b
fix: don't assert on _api_*
Skn0tt Apr 26, 2023
8664788
fix: another test
Skn0tt Apr 26, 2023
1ed2b9c
fix: apply NODE_ENV=prod to right generated handler
Skn0tt Apr 26, 2023
8807dd6
Merge branch 'main' into split-api-routes
Skn0tt Apr 27, 2023
eef0c6c
feat: remove nft tracing
Skn0tt May 2, 2023
0f5a144
feat: put change behind flag again
Skn0tt May 2, 2023
ea47ceb
feat: source flag from plugin input
Skn0tt May 2, 2023
cf24ee1
fix: add default value for featureflags
Skn0tt May 2, 2023
d757ead
Merge branch 'main' into split-api-routes
Skn0tt May 3, 2023
bb4d4cf
fix: default flag to true for testing
Skn0tt May 3, 2023
bf2983b
fix: revert changes to lockfiles
Skn0tt May 3, 2023
34257d8
fix: eslint it/test
Skn0tt May 3, 2023
c8e728c
fix: lint
Skn0tt May 3, 2023
52b79f8
fix: revert distracting change
Skn0tt May 3, 2023
2a4ceae
fix: now that we don't use nft anymore, we don't have to copy over no…
Skn0tt May 3, 2023
9d76dd4
Merge remote-tracking branch 'origin/main' into split-api-routes
nickytonline May 3, 2023
c896db1
fix: swallow require.resolve errors for unit tests
Skn0tt May 4, 2023
16aa3a1
fix: remove timing logs
Skn0tt May 4, 2023
dbaf631
fix: lint name
Skn0tt May 4, 2023
25190c6
Merge branch 'main' into split-api-routes
Skn0tt May 5, 2023
84cceb3
fix: lint
Skn0tt May 5, 2023
b09317e
fix: isr needs _document.js
Skn0tt May 5, 2023
3c19e25
fix: add _app for ISR
Skn0tt May 5, 2023
157f29d
fix: correct wrong output of some npm versions
Skn0tt May 5, 2023
78a2753
fix: integration test
Skn0tt May 5, 2023
fc371c3
fix: assemble npm package path correctly, also for monorepos
Skn0tt May 5, 2023
5131d74
fix: try what happens if we use next-netlify server
Skn0tt May 8, 2023
7a8df05
fix: check what happens when we skip revalidation
Skn0tt May 8, 2023
df0ea5a
fix: dont let revalidate request time out
Skn0tt May 8, 2023
25e8a99
fix: send x-nextjs-cache header to prevent error
Skn0tt May 8, 2023
c78e10d
fix: update error message in test
Skn0tt May 8, 2023
9216fea
fix: resolve relative paths based on data in required-server-files
Skn0tt May 8, 2023
d40beb0
fix: test
Skn0tt May 8, 2023
71f7bf2
fix: keep manually-added `included_files`
Skn0tt May 8, 2023
29a40a7
Merge branch 'main' into split-api-routes
Skn0tt May 8, 2023
118bf89
fix: try something
Skn0tt May 8, 2023
008014a
fix: don't include unneeded _app pages in ISR
Skn0tt May 8, 2023
0a44c6d
fix: finalize includedFiles before writing it onto netlifyConfig
Skn0tt May 8, 2023
0f4b522
chore: update comment
Skn0tt May 8, 2023
b799a19
fix: exclude sass file in monorepos
Skn0tt May 8, 2023
70a4fb2
Update packages/runtime/src/helpers/functions.ts
Skn0tt May 8, 2023
89fd2fb
chore: remove comment
Skn0tt May 8, 2023
f9f726f
fix: update flag impl
Skn0tt May 11, 2023
88f9b3e
refactor: use getRequiredServerFiles
Skn0tt May 11, 2023
223990e
chore: add comment on route[0]
Skn0tt May 11, 2023
6089296
fix: set NEXT_SPLIT_API_ROUTES in netlify.toml
Skn0tt May 11, 2023
30db86a
fix: put updated revalidate behaviour behind flag
Skn0tt May 11, 2023
e3c2c4d
fix: supply splitApiRoutes in getHandler
Skn0tt May 11, 2023
6287e15
fix: better run your code before committing it and embarrassing yourself
Skn0tt May 11, 2023
fdcf8d5
Merge branch 'main' into split-api-routes
Skn0tt May 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/e2e-appdir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
push:
branches: [main]

env:
NEXT_SPLIT_API_ROUTES: true

jobs:
setup:
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/e2e-next.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
schedule:
- cron: '0 0 * * *'

env:
NEXT_SPLIT_API_ROUTES: true

jobs:
setup:
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

env:
NEXT_SPLIT_API_ROUTES: true

jobs:
build:
name: Integration tests
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

env:
NEXT_SPLIT_API_ROUTES: true

jobs:
build:
name: Unit tests
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,6 @@ packages/*/lib
cypress/screenshots

# Test cases have node module fixtures
!test/**/node_modules
!test/**/node_modules

/tmp
2 changes: 1 addition & 1 deletion cypress/e2e/default/revalidate.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('On-demand revalidation', () => {
cy.request({ url: '/api/revalidate/?select=5', failOnStatusCode: false }).then((res) => {
expect(res.status).to.eq(500)
expect(res.body).to.have.property('message')
expect(res.body.message).to.include('Invalid response 404')
expect(res.body.message).to.include('could not refresh content for path /getStaticProps/withRevalidate/3/, path is not handled by an odb')
pieh marked this conversation as resolved.
Show resolved Hide resolved
})
})
it('revalidates dynamic non-prerendered ISR route with fallback blocking', () => {
Expand Down
1 change: 1 addition & 0 deletions demos/default/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CYPRESS_CACHE_FOLDER = "../node_modules/.CypressBinary"
# set TERM variable for terminal output
TERM = "xterm"
NODE_VERSION = "16.15.1"
NEXT_SPLIT_API_ROUTES = "true"

[[headers]]
for = "/_next/image/*"
Expand Down
3 changes: 3 additions & 0 deletions demos/middleware/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ command = "npm run build"
publish = ".next"
ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../..; fi;"

[build.environment]
NEXT_SPLIT_API_ROUTES = "true"

[[plugins]]
package = "@netlify/plugin-nextjs"

Expand Down
3 changes: 3 additions & 0 deletions demos/nx-next-monorepo-demo/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ command = "npm run build"
publish = "dist/apps/demo-monorepo/.next"
ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../..; fi;"

[build.environment]
NEXT_SPLIT_API_ROUTES = "true"

[dev]
command = "npm run start"
targetPort = 4200
Expand Down
3 changes: 3 additions & 0 deletions demos/static-root/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ command = "next build"
publish = ".next"
ignore = "if [ $CACHED_COMMIT_REF == $COMMIT_REF ]; then (exit 1); else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ../..; fi;"

[build.environment]
NEXT_SPLIT_API_ROUTES = "true"

[[plugins]]
package = "@netlify/plugin-nextjs"

Expand Down
26 changes: 24 additions & 2 deletions packages/runtime/src/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import slash from 'slash'

import { HANDLER_FUNCTION_NAME, IMAGE_FUNCTION_NAME, ODB_FUNCTION_NAME } from '../constants'

import { splitApiRoutes } from './flags'
import type { APILambda } from './functions'
import type { RoutesManifest } from './types'
import { escapeStringRegexp } from './utils'

Expand All @@ -17,6 +19,7 @@ type NetlifyHeaders = NetlifyConfig['headers']

export interface RequiredServerFiles {
version?: number
relativeAppDir?: string
config?: NextConfigComplete
appDir?: string
files?: string[]
Expand Down Expand Up @@ -98,10 +101,14 @@ export const configureHandlerFunctions = async ({
netlifyConfig,
publish,
ignore = [],
apiLambdas,
featureFlags,
}: {
netlifyConfig: NetlifyConfig
publish: string
ignore: Array<string>
apiLambdas: APILambda[]
featureFlags: Record<string, unknown>
}) => {
const config = await getRequiredServerFiles(publish)
const files = config.files || []
Expand All @@ -117,7 +124,7 @@ export const configureHandlerFunctions = async ({
(moduleName) => !hasManuallyAddedModule({ netlifyConfig, moduleName }),
)

;[HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, '_api_*'].forEach((functionName) => {
const configureFunction = (functionName: string) => {
netlifyConfig.functions[functionName] ||= { included_files: [], external_node_modules: [] }
netlifyConfig.functions[functionName].node_bundler = 'nft'
netlifyConfig.functions[functionName].included_files ||= []
Expand Down Expand Up @@ -156,7 +163,22 @@ export const configureHandlerFunctions = async ({
netlifyConfig.functions[functionName].included_files.push(`!${moduleRoot}/**/*`)
}
})
})
}

configureFunction(HANDLER_FUNCTION_NAME)
configureFunction(ODB_FUNCTION_NAME)

if (splitApiRoutes(featureFlags)) {
for (const apiLambda of apiLambdas) {
const { functionName, includedFiles } = apiLambda
netlifyConfig.functions[functionName] ||= { included_files: [] }
netlifyConfig.functions[functionName].node_bundler = 'none'
netlifyConfig.functions[functionName].included_files ||= []
netlifyConfig.functions[functionName].included_files.push(...includedFiles)
}
} else {
configureFunction('_api_*')
}
}

interface BuildHeaderParams {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/helpers/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ export const getDependenciesOfFile = async (file: string) => {
if (!existsSync(nft)) {
return []
}
const dependencies = await readJson(nft, 'utf8')
const dependencies = (await readJson(nft, 'utf8')) as { files: string[] }
return dependencies.files.map((dep) => resolve(dirname(file), dep))
}

Expand Down
17 changes: 17 additions & 0 deletions packages/runtime/src/helpers/flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import destr from 'destr'

/**
* If this flag is enabled, we generate individual Lambda functions for API Routes.
* They're packed together in 50mb chunks to avoid hitting the Lambda size limit.
*
* To prevent bundling times from rising,
* we use the "none" bundling strategy where we fully rely on Next.js' `.nft.json` files.
* This should to a significant speedup, but is still experimental.
*
* If disabled, we bundle all API Routes into a single function.
* This is can lead to large bundle sizes.
*
* Disabled by default. Can be overriden using the NEXT_SPLIT_API_ROUTES env var.
*/
export const splitApiRoutes = (featureFlags: Record<string, unknown>): boolean =>
destr(process.env.NEXT_SPLIT_API_ROUTES) ?? featureFlags.next_split_api_routes ?? false
Loading