Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Edge Functions / Middleware support (#9)
* experiment: use from npm so we can use next 12 middleware * use local @happykit/flags * use @happykit/flags through workspace * use req.cookies on server * experimental middleware support * add middleware pages for all variants * move Content to components * Content -> EdgeFunctionContent * bring back dynamic recompliation of package upgrades @preconstruct/next to a version supporting Next.js 12 * use response.cookie response.cookie was originally broken when Expires was present on the cookie: vercel/next.js#30430 This will be fixed in Next.js v12.0.2, so we use the canary of that for now, and the release of happykit might have to be a breaking change since we then no longer support older Next.js versions theoretically. We need to list next >=12.0.2 as a peerDependency once v12.0.2 is out. * use getCookie * update dependencies * pre -> code * use differt header access methods * switch prebuild to preconstruct build * use next.js v12.0.2 * upgrade dependencies * add @tailwindui/react * upgrade @babel/preset-env * disable dynamic recompilation Did not work, so reverting back to triggering package builds manually. * add reload button to middleware * rename happykit.config to flags.config * increase maxAge * add flagBag.cookie to getEdgeFlags * avoid eval/new Function warning Middleware does not accept eval or new Function, but the way in which we create our custom errors uses that under the hood. So we need to throw native errors directly without extending the error class. * keep bottom bar on bottom even with short content like on the middleware demo page * list middleware support as key feature * add getEdgeFlags to README * remove InvalidConfigurationError * reenable preconstruct dev * Revert "reenable preconstruct dev" This reverts commit 2171933. * recreate cookieOptions object on every request This avoids vercel/next.js#31666 * explain middleware behaviour in EdgeFunctionContent * consistently use flags.config * make flagBag.cookie iterable * Revert "make flagBag.cookie iterable" This reverts commit 77bfb6f. * reword key features * remove jest-expect-message * add edge tests * v2.0.0
- Loading branch information
Showing
30 changed files
with
2,398 additions
and
1,275 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import React from "react"; | ||
import { AppFlags } from "../types/AppFlags"; | ||
|
||
export function EdgeFunctionContent(props: { | ||
checkoutVariant: AppFlags["checkout"]; | ||
}) { | ||
return ( | ||
<React.Fragment> | ||
<p> | ||
This demo shows how to use <code>@happykit/flags</code> in{" "} | ||
<a | ||
href="https://nextjs.org/blog/next-12#introducing-middleware" | ||
rel="noreferrer noopener" | ||
> | ||
Next.js Middleware | ||
</a> | ||
. | ||
</p> | ||
<pre> | ||
You have been served the "<code>{props.checkoutVariant}</code>" checkout | ||
variant. | ||
</pre> | ||
<button | ||
type="button" | ||
className="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" | ||
onClick={() => { | ||
document.cookie = | ||
"hkvk=; path=/; maxAge=0; expires=Thu, 01 Jan 1970 00:00:01 GMT"; | ||
window.location.reload(); | ||
}} | ||
> | ||
Remove cookie and reload | ||
</button> | ||
<p> | ||
This example uses a <code>_middleware</code> file at{" "} | ||
<code>pages/demo/middleware</code> to statically render different | ||
variants for the <code>/demo/middleware</code> path. The different | ||
variants live under <code>pages/demo/middleware/variant-*.tsx</code>. | ||
</p> | ||
<p> | ||
The middleware loads the flags and rewrites the incoming request either | ||
to <code>variant-short.tsx</code>, <code>variant-medium.tsx</code> or{" "} | ||
<code>variant-full.tsx</code> depending on the resolved flag variant. | ||
</p> | ||
<p> | ||
Since resulting page is served statically from the edge, rendering will | ||
use no visitor key. This is necessary as the concept of a visitor does | ||
not exist during static site generation. Thus all rules and | ||
percentage-based rollouts targeting a visitor resolve to{" "} | ||
<code>null</code>. | ||
</p> | ||
<p> | ||
The middleware loads the flags purely to decide where to rewrite the | ||
request to. It does not send any resolved flags into the application | ||
itself. | ||
</p> | ||
<p> | ||
You are however free to call <code>useFlags()</code> on the client and | ||
combine this approach with the middleware. | ||
</p> | ||
</React.Fragment> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { configure } from "@happykit/flags/config"; | ||
|
||
configure({ | ||
envKey: process.env.NEXT_PUBLIC_FLAGS_ENV_KEY!, | ||
|
||
// You can just delete this line in your own application. | ||
// It's only here because we use it while working on @happykit/flags itself. | ||
endpoint: process.env.NEXT_PUBLIC_FLAGS_ENDPOINT, | ||
}); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import type { AppFlags } from "../../../types/AppFlags"; | ||
// Importing the config is necessary to configure getEdgeFlags | ||
import "../../../flags.config"; | ||
import { getEdgeFlags } from "@happykit/flags/edge"; | ||
|
||
export async function middleware(request: NextRequest) { | ||
const flagBag = await getEdgeFlags<AppFlags>({ request }); | ||
|
||
const response = NextResponse.rewrite( | ||
`/demo/middleware/variant-${flagBag.flags?.checkout || "full"}` | ||
); | ||
|
||
if (flagBag.cookie) response.cookie(...flagBag.cookie.args); | ||
|
||
return response; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import * as React from "react"; | ||
import { Layout } from "../../../components/Layout"; | ||
import { EdgeFunctionContent } from "../../../components/EdgeFunctionContent"; | ||
|
||
export default function Page() { | ||
return ( | ||
<Layout | ||
title="Middleware" | ||
source={`https://github.com/happykit/flags/blob/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF}/example/pages/demo/middleware`} | ||
flagBag={null} | ||
> | ||
<article className="py-4 prose max-w-prose"> | ||
<EdgeFunctionContent checkoutVariant="full" /> | ||
</article> | ||
</Layout> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import * as React from "react"; | ||
import { Layout } from "../../../components/Layout"; | ||
import { EdgeFunctionContent } from "../../../components/EdgeFunctionContent"; | ||
|
||
export default function Page() { | ||
return ( | ||
<Layout | ||
title="Middleware" | ||
source={`https://github.com/happykit/flags/blob/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF}/example/pages/demo/middleware`} | ||
flagBag={null} | ||
> | ||
<article className="py-4 prose max-w-prose"> | ||
<EdgeFunctionContent checkoutVariant="medium" /> | ||
</article> | ||
</Layout> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import * as React from "react"; | ||
import { Layout } from "../../../components/Layout"; | ||
import { EdgeFunctionContent } from "../../../components/EdgeFunctionContent"; | ||
|
||
export default function Page() { | ||
return ( | ||
<Layout | ||
title="Middleware" | ||
source={`https://github.com/happykit/flags/blob/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF}/example/pages/demo/middleware`} | ||
flagBag={null} | ||
> | ||
<article className="py-4 prose max-w-prose"> | ||
<EdgeFunctionContent checkoutVariant="short" /> | ||
</article> | ||
</Layout> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
1dc1653
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: