Skip to content

Commit 50510da

Browse files
make site work with the Cloudflare OpenNext adapter
update the site application so that it can be build using the Cloudflare OpenNext adapter (`@opennextjs/cloudflare`) and thus deployed on Cloudflare Workers
1 parent 91544e7 commit 50510da

File tree

17 files changed

+4746
-974
lines changed

17 files changed

+4746
-974
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,11 @@ cache
3535
tsconfig.tsbuildinfo
3636

3737
dist/
38+
39+
# Ignore the blog-data json that we generate during dev and build
40+
apps/site/public/blog-data.json
41+
42+
# Cloudflare Build Output
43+
apps/site/.open-next
44+
apps/site/.wrangler
45+

CONTRIBUTING.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Thank you for your interest in contributing to the Node.js Website. Before you p
77
- [Becoming a collaborator](#becoming-a-collaborator)
88
- [Getting started](#getting-started)
99
- [CLI Commands](#cli-commands)
10+
- [Cloudflare Deployment](#cloudflare-deployment)
1011
- [Commit Guidelines](#commit-guidelines)
1112
- [Pull Request Policy](#pull-request-policy)
1213
- [Developer's Certificate of Origin 1.1](#developers-certificate-of-origin-11)
@@ -165,6 +166,42 @@ This repository contains several scripts and commands for performing numerous ta
165166

166167
</details>
167168

169+
## Cloudflare Deployment
170+
171+
The Node.js Website can be deployed to the [Cloudflare](https://www.cloudflare.com) network using [Cloudflare Workers](https://www.cloudflare.com/en-gb/developer-platform/products/workers/) and the [OpenNext Cloudflare adapter](https://opennext.js.org/cloudflare). This section provides the necessary details for testing and deploying the website on Cloudflare.
172+
173+
### Scripts
174+
175+
Preview and deployment of the website targeting the Cloudflare network is implemented via the following two commands:
176+
177+
- `pnpm cloudflare:preview` builds the website using the OpenNext Cloudflare adapter and runs the website locally in a server simulating the Cloudflare hosting (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/))
178+
- `pnpm cloudflare:deploy` builds the website using the OpenNext Cloudflare adapter and deploys the website to the Cloudflare network (using the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/))
179+
180+
### Configurations
181+
182+
There are two key configuration files related to Cloudflare deployment.
183+
184+
#### Wrangler Configuration
185+
186+
This file defines the settings for the Cloudflare Worker, which serves the website.
187+
188+
For more details, refer to the [Wrangler documentation](https://developers.cloudflare.com/workers/wrangler/configuration/).
189+
190+
Key configurations include:
191+
192+
- `main`: Points to the worker generated by the OpenNext adapter.
193+
- `account_id`: Specifies the Cloudflare account ID. This is not required for local previews but is necessary for deployments. You can obtain an account ID for free by signing up at [dash.cloudflare.com](https://dash.cloudflare.com/login).
194+
- `build`: Defines the build command to generate Node.js filesystem polyfills required for the application to run on Cloudflare Workers. This uses the [`@flarelabs/wrangler-build-time-fs-assets-polyfilling`](https://github.com/flarelabs-net/wrangler-build-time-fs-assets-polyfilling) package.
195+
- `alias`: Maps aliases for the Node.js filesystem polyfills generated during the build process.
196+
- `kv_namespaces`: Contains a single KV binding definition for `NEXT_CACHE_WORKERS_KV`. This is used to implement the Next.js incremental cache. For deployments, you can create a new KV namespace in the Cloudflare dashboard and update the binding ID accordingly.
197+
198+
#### OpenNext Configuration
199+
200+
This is the configuration for the OpenNext Cloudflare adapter, for more details on such configuration please refer to the [official OpenNext documentation](https://opennext.js.org/cloudflare/get-started#4-add-an-open-nextconfigts-file).
201+
202+
The configuration present here is very standard and simply sets up incremental cache via the KV binding
203+
defined in the wrangler configuration file.
204+
168205
## Commit Guidelines
169206

170207
This project follows the [Conventional Commits][] specification.

apps/site/.stylelintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ lcov.info
1313

1414
# Old Styles
1515
styles/old
16+
17+
# Cloudflare Build Output
18+
.open-next
19+
.wrangler

apps/site/app/[locale]/next-data/blog-data/[category]/[page]/route.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

apps/site/layouts/Blog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import type { BlogCategory } from '#site/types';
1111

1212
import styles from './layouts.module.css';
1313

14-
const getBlogCategory = async (pathname: string) => {
14+
const getBlogCategory = (pathname: string) => {
1515
// pathname format can either be: /en/blog/{category}
1616
// or /en/blog/{category}/page/{page}
1717
// hence we attempt to interpolate the full /en/blog/{category}/page/{page}
1818
// and in case of course no page argument is provided we define it to 1
1919
// note that malformed routes can't happen as they are all statically generated
2020
const [, , category = 'all', , page = 1] = pathname.split('/');
2121

22-
const { posts, pagination } = await getBlogData(
22+
const { posts, pagination } = getBlogData(
2323
category as BlogCategory,
2424
Number(page)
2525
);
@@ -38,7 +38,7 @@ const BlogLayout: FC = async () => {
3838
link: `/blog/${category}`,
3939
}));
4040

41-
const blogData = await getBlogCategory(pathname);
41+
const blogData = getBlogCategory(pathname);
4242

4343
return (
4444
<>

apps/site/next-data/blogData.ts

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,16 @@
1-
import {
2-
ENABLE_STATIC_EXPORT,
3-
NEXT_DATA_URL,
4-
IS_NOT_VERCEL_RUNTIME_ENV,
5-
} from '#site/next.constants.mjs';
61
import type { BlogCategory, BlogPostsRSC } from '#site/types';
72

8-
const getBlogData = (
9-
cat: BlogCategory,
10-
page?: number
11-
): Promise<BlogPostsRSC> => {
12-
// When we're using Static Exports the Next.js Server is not running (during build-time)
13-
// hence the self-ingestion APIs will not be available. In this case we want to load
14-
// the data directly within the current thread, which will anyways be loaded only once
15-
// We use lazy-imports to prevent `provideBlogData` from executing on import
16-
if (ENABLE_STATIC_EXPORT || IS_NOT_VERCEL_RUNTIME_ENV) {
17-
return import('#site/next-data/providers/blogData').then(
18-
({ provideBlogPosts, providePaginatedBlogPosts }) =>
19-
page ? providePaginatedBlogPosts(cat, page) : provideBlogPosts(cat)
20-
);
21-
}
22-
23-
const fetchURL = `${NEXT_DATA_URL}blog-data/${cat}/${page ?? 0}`;
3+
import {
4+
provideBlogPosts,
5+
providePaginatedBlogPosts,
6+
} from './providers/blogData';
247

25-
// This data cannot be cached because it is continuously updated. Caching it would lead to
26-
// outdated information being shown to the user.
27-
return fetch(fetchURL)
28-
.then(response => response.text())
29-
.then(JSON.parse);
8+
const getBlogData = (cat: BlogCategory, page?: number): BlogPostsRSC => {
9+
return page && page >= 1
10+
? // This allows us to blindly get all blog posts from a given category
11+
// if the page number is 0 or something smaller than 1
12+
providePaginatedBlogPosts(cat, page)
13+
: provideBlogPosts(cat);
3014
};
3115

3216
export default getBlogData;

apps/site/next-data/providers/blogData.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { cache } from 'react';
22

3-
import generateBlogData from '#site/next-data/generators/blogData.mjs';
43
import { BLOG_POSTS_PER_PAGE } from '#site/next.constants.mjs';
4+
import { blogData } from '#site/next.json.mjs';
55
import type { BlogCategory, BlogPostsRSC } from '#site/types';
66

7-
const { categories, posts } = await generateBlogData();
8-
9-
export const provideBlogCategories = cache(() => categories);
7+
const blogPosts = cache(() =>
8+
blogData.posts.map(post => ({
9+
...post,
10+
date: new Date(post.date),
11+
}))
12+
);
1013

1114
export const provideBlogPosts = cache(
1215
(category: BlogCategory): BlogPostsRSC => {
13-
const categoryPosts = posts
16+
const categoryPosts = blogPosts()
1417
.filter(post => post.categories.includes(category))
1518
.sort((a, b) => b.date.getTime() - a.date.getTime());
1619

apps/site/next.dynamic.constants.mjs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
'use strict';
22

3-
import {
4-
provideBlogCategories,
5-
provideBlogPosts,
6-
} from './next-data/providers/blogData';
3+
import { blogData } from '#site/next.json.mjs';
4+
5+
import { provideBlogPosts } from './next-data/providers/blogData';
76
import { BASE_PATH, BASE_URL } from './next.constants.mjs';
87
import { siteConfig } from './next.json.mjs';
98
import { defaultLocale } from './next.locales.mjs';
@@ -31,9 +30,9 @@ export const IGNORED_ROUTES = [
3130
*/
3231
export const DYNAMIC_ROUTES = new Map([
3332
// Provides Routes for all Blog Categories
34-
...provideBlogCategories().map(c => [`blog/${c}`, 'blog-category']),
33+
...blogData.categories.map(c => [`blog/${c}`, 'blog-category']),
3534
// Provides Routes for all Blog Categories w/ Pagination
36-
...provideBlogCategories()
35+
...blogData.categories
3736
// retrieves the amount of pages for each blog category
3837
.map(c => [c, provideBlogPosts(c).pagination.pages])
3938
// creates a numeric array for each page and define a pathname for

apps/site/next.json.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import _authors from './authors.json' with { type: 'json' };
44
import _siteNavigation from './navigation.json' with { type: 'json' };
5+
import _blogData from './public/blog-data.json' with { type: 'json' };
56
import _siteRedirects from './redirects.json' with { type: 'json' };
67
import _siteConfig from './site.json' with { type: 'json' };
78

@@ -16,3 +17,6 @@ export const siteRedirects = _siteRedirects;
1617

1718
/** @type {import('./types').SiteConfig} */
1819
export const siteConfig = _siteConfig;
20+
21+
/** @type {import('./types').BlogData} */
22+
export const blogData = _blogData;

apps/site/open-next.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { defineCloudflareConfig } from '@opennextjs/cloudflare';
2+
import incrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache';
3+
4+
export default defineCloudflareConfig({ incrementalCache });

0 commit comments

Comments
 (0)