Skip to content

Commit 727f4e2

Browse files
committed
chore: Add CORS example
1 parent 4bf825f commit 727f4e2

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

examples/cors/index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Document</title>
7+
<style>
8+
pre {
9+
white-space: pre-wrap;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<p>/no-cors</p>
15+
<pre id="noCorsResponse">Loading...</pre>
16+
<hr />
17+
<p>/api/cors</p>
18+
<pre id="corsResponse">Loading...</pre>
19+
<hr />
20+
<p>/api/other</p>
21+
<pre id="otherResponse">Loading...</pre>
22+
23+
<script>
24+
async function fetchEndpoint(endpoint, pre) {
25+
try {
26+
const res = await fetch(`http://localhost:3000${endpoint}`);
27+
const text = await res.text();
28+
29+
const meta = JSON.stringify(
30+
{
31+
status: res.status,
32+
statusText: res.statusText,
33+
headers: Object.fromEntries(res.headers),
34+
},
35+
null,
36+
2,
37+
);
38+
pre.textContent = `${meta}\n${text}`;
39+
} catch (err) {
40+
pre.textContent = String(err);
41+
}
42+
}
43+
44+
fetchEndpoint("/no-cors", noCorsResponse);
45+
fetchEndpoint("/api/cors", corsResponse);
46+
fetchEndpoint("/api/other", otherResponse);
47+
</script>
48+
</body>
49+
</html>

examples/cors/main.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { createApp } from "@aklinker1/zeta";
2+
import dedent from "dedent";
3+
import testPage from "./index.html";
4+
5+
export const corsPlugin = createApp()
6+
.onGlobalRequest(({ method, path, set }) => {
7+
// NOTE: The `onGlobalRequest` hook is used because it runs on every
8+
// request, including child apps. Read the docs for more details on global
9+
// hooks: https://zeta.aklinker1.io/server/hooks/#global-hooks
10+
//
11+
// That means if you want to filter which endpoints have CORS enabled, you
12+
// should do that in this callback using the method and path from the
13+
// request context. Just return before applying any headers to disable CORS.
14+
if (!path.startsWith("/api")) return;
15+
16+
// Set CORS headers on all requests
17+
set.headers["Access-Control-Allow-Origin"] = "*";
18+
set.headers["Access-Control-Allow-Methods"] = "GET";
19+
// ...
20+
21+
// Shortcircuit OPTIONS requests by returning a `Response` instance, don't
22+
// call any handlers if defined. For more details, read the docs:
23+
// https://zeta.aklinker1.io/server/hooks/ontransform/#short-circuiting
24+
if (method === "OPTIONS") return new Response();
25+
})
26+
.export();
27+
28+
const apiApp = createApp({ prefix: "/api" }).get("/cors", () => "OK");
29+
30+
const app = createApp()
31+
.use(corsPlugin)
32+
.use(apiApp)
33+
.get("/no-cors", () => "OK");
34+
35+
// Serve the test page and backend on :3000
36+
const fetch = app.build();
37+
Bun.serve({
38+
routes: {
39+
"/": testPage,
40+
"/**": fetch,
41+
},
42+
});
43+
44+
// Serve the test HTML page on :3001, a different origin.
45+
Bun.serve({
46+
port: 3001,
47+
routes: {
48+
"/": testPage,
49+
},
50+
});
51+
52+
console.log(dedent`
53+
Example servers started!
54+
55+
Open the http://localhost:3000 and http://localhost:3001 to see CORS in
56+
action.
57+
58+
Both pages make fetch requests to http://localhost:3000/*.
59+
http://localhost:3000 won't have any problems making the 3 test requests, but
60+
one of the requests on http://localhost:3001 will fail due to CORS because
61+
it's path doesn't start with "/api".
62+
`);

0 commit comments

Comments
 (0)