Skip to content

Commit d7f6572

Browse files
introduce new initOpenNextCloudflareForDev utility and make getCloudflareContext synchronous
1 parent 6e58093 commit d7f6572

19 files changed

+370
-105
lines changed

.changeset/chilly-dryers-begin.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
introduce new `initOpenNextCloudflareForDev` utility and make `getCloudflareContext` synchronous
6+
7+
introduce a new `initOpenNextCloudflareForDev` function that when called in the [Next.js config file](https://nextjs.org/docs/app/api-reference/config/next-config-js) integrates the Next.js dev server with the open-next Cloudflare adapter. Most noticeably this enables `getCloudflareContext` to work in
8+
the edge runtime (including middlewares) during development.
9+
10+
Moving forward we'll recommend that all applications include the use of this utility in their config file (there is no downside in doing so and it only effect local development).
11+
12+
Example:
13+
14+
```js
15+
// next.config.mjs
16+
17+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
18+
19+
/** @type {import('next').NextConfig} */
20+
const nextConfig = {};
21+
22+
initOpenNextCloudflareForDev();
23+
24+
export default nextConfig;
25+
```
26+
27+
thanks to this new utility we are also able to make `getCloudflareContext` synchronous, so `await`ing such function is no longer necessary
28+
29+
Example:
30+
31+
- Before:
32+
33+
```js
34+
import { getCloudflareContext } from "@opennextjs/cloudflare";
35+
36+
const cloudflareContext = await getCloudflareContext();
37+
// cloudflareContext use
38+
```
39+
40+
- After:
41+
42+
```js
43+
import { getCloudflareContext } from "@opennextjs/cloudflare";
44+
45+
const cloudflareContext = getCloudflareContext();
46+
// cloudflareContext use
47+
```

examples/api/e2e/playwright.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,8 @@ export default defineConfig({
4949
command: "pnpm preview:worker",
5050
url: "http://localhost:8770",
5151
reuseExistingServer: !process.env.CI,
52+
// the app uses the `initOpenNextCloudflareForDev` which in CI apparently makes the boot up take slightly longer,
53+
// that's why we need a longer timeout here (we just add 10 seconds to the default 60)
54+
timeout: !process.env.CI ? undefined : 60 * 1000 + 10 * 1000,
5255
},
5356
});

examples/api/next.config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
2+
3+
initOpenNextCloudflareForDev();
4+
15
/** @type {import('next').NextConfig} */
26
const nextConfig = {};
37

examples/create-next-app/e2e/playwright.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,8 @@ export default defineConfig({
4949
command: "pnpm preview:worker",
5050
url: "http://localhost:8771",
5151
reuseExistingServer: !process.env.CI,
52+
// the app uses the `initOpenNextCloudflareForDev` which in CI apparently makes the boot up take slightly longer,
53+
// that's why we need a longer timeout here (we just add 10 seconds to the default 60)
54+
timeout: !process.env.CI ? undefined : 60 * 1000 + 10 * 1000,
5255
},
5356
});

examples/create-next-app/next.config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
2+
3+
initOpenNextCloudflareForDev();
4+
15
/** @type {import('next').NextConfig} */
26
const nextConfig = {};
37

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
import { headers } from "next/headers";
2+
13
export default function MiddlewarePage() {
2-
return <h1>Via middleware</h1>;
4+
const cloudflareContextHeader = headers().get("x-cloudflare-context");
5+
6+
return (
7+
<>
8+
<h1>Via middleware</h1>
9+
<p>
10+
The value of the <i>x-cloudflare-context</i> header is: <br />
11+
<span
12+
style={{
13+
display: "inline-block",
14+
margin: "1rem 2rem",
15+
color: "grey",
16+
fontSize: "1.2rem",
17+
}}
18+
data-testid="cloudflare-context-header"
19+
>
20+
{cloudflareContextHeader}
21+
</span>
22+
</p>
23+
</>
24+
);
325
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test("cloudflare context env object is populated", async ({ page }) => {
4+
await page.goto("/middleware");
5+
const cloudflareContextHeaderElement = page.getByTestId("cloudflare-context-header");
6+
// Note: the text in the span is "keys of `cloudflareContext.env`: MY_VAR, MY_KV, ASSETS" for previews
7+
// and "keys of `cloudflareContext.env`: MY_VAR, MY_KV" in dev (`next dev`)
8+
// that's why we use `toContain` instead of `toEqual`, this is incorrect and the `ASSETS` binding
9+
// should ideally also be part of the dev cloudflare context
10+
// (this is an upstream wrangler issue: https://github.com/cloudflare/workers-sdk/issues/7812)
11+
expect(await cloudflareContextHeaderElement.textContent()).toContain(
12+
"keys of `cloudflareContext.env`: MY_VAR, MY_KV"
13+
);
14+
});

examples/middleware/e2e/playwright.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,8 @@ export default defineConfig({
4949
command: "pnpm preview:worker",
5050
url: "http://localhost:8774",
5151
reuseExistingServer: !process.env.CI,
52+
// the app uses the `initOpenNextCloudflareForDev` which in CI apparently makes the boot up take slightly longer,
53+
// that's why we need a longer timeout here (we just add 10 seconds to the default 60)
54+
timeout: !process.env.CI ? undefined : 60 * 1000 + 10 * 1000,
5255
},
5356
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { defineConfig, devices } from "@playwright/test";
2+
3+
declare var process: { env: Record<string, string> };
4+
5+
/**
6+
* See https://playwright.dev/docs/test-configuration.
7+
*/
8+
export default defineConfig({
9+
testDir: "./",
10+
/* Run tests in files in parallel */
11+
fullyParallel: true,
12+
/* Fail the build on CI if you accidentally left test.only in the source code. */
13+
forbidOnly: !!process.env.CI,
14+
/* Retry on CI only */
15+
retries: process.env.CI ? 2 : 0,
16+
/* Opt out of parallel tests on CI. */
17+
workers: process.env.CI ? 1 : undefined,
18+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
19+
reporter: "html",
20+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
21+
use: {
22+
/* Base URL to use in actions like `await page.goto('/')`. */
23+
baseURL: "http://localhost:3334",
24+
25+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
26+
trace: "on-first-retry",
27+
},
28+
29+
/* Configure projects for major browsers */
30+
projects: [
31+
{
32+
name: "chromium",
33+
use: { ...devices["Desktop Chrome"] },
34+
},
35+
36+
{
37+
name: "firefox",
38+
use: { ...devices["Desktop Firefox"] },
39+
},
40+
41+
{
42+
name: "webkit",
43+
use: { ...devices["Desktop Safari"] },
44+
},
45+
],
46+
47+
/* Run your local dev server before starting the tests */
48+
webServer: {
49+
command: "pnpm dev --port 3334",
50+
url: "http://localhost:3334",
51+
reuseExistingServer: !process.env.CI,
52+
},
53+
});

examples/middleware/middleware.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { NextRequest, NextResponse, NextFetchEvent } from "next/server";
22
import { clerkMiddleware } from "@clerk/nextjs/server";
33

4-
export function middleware(request: NextRequest, event: NextFetchEvent) {
4+
import { getCloudflareContext } from "@opennextjs/cloudflare";
5+
6+
export async function middleware(request: NextRequest, event: NextFetchEvent) {
57
console.log("middleware");
68
if (request.nextUrl.pathname === "/about") {
79
return NextResponse.redirect(new URL("/redirected", request.url));
@@ -16,7 +18,19 @@ export function middleware(request: NextRequest, event: NextFetchEvent) {
1618
})(request, event);
1719
}
1820

19-
return NextResponse.next();
21+
const requestHeaders = new Headers(request.headers);
22+
const cloudflareContext = getCloudflareContext();
23+
24+
requestHeaders.set(
25+
"x-cloudflare-context",
26+
`keys of \`cloudflareContext.env\`: ${Object.keys(cloudflareContext.env).join(", ")}`
27+
);
28+
29+
return NextResponse.next({
30+
request: {
31+
headers: requestHeaders,
32+
},
33+
});
2034
}
2135

2236
export const config = {

0 commit comments

Comments
 (0)