-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Added a hono version of workers examples #21258
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
Changes from 2 commits
60b8da3
2c01742
3417ab1
22a1519
3583c21
f202f1f
06a26d0
4885dba
5ee2391
6cdc391
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,6 +56,32 @@ export default { | |
| } satisfies ExportedHandler; | ||
| ``` | ||
|
|
||
| </TabItem> <TabItem label="Hono" icon="seti:typescript"> | ||
|
|
||
| ```ts | ||
| import { Hono } from "hono"; | ||
|
|
||
| type Bindings = {}; | ||
|
||
|
|
||
| const app = new Hono<{ Bindings: Bindings }>(); | ||
|
|
||
| app.get("*", async (c) => { | ||
| // Access the raw request to get the cf object | ||
| const req = c.req.raw; | ||
|
|
||
| // Check if the cf object is available | ||
| const data = | ||
| req.cf !== undefined | ||
| ? req.cf | ||
| : { error: "The `cf` object is not available inside the preview." }; | ||
|
|
||
| // Return the data formatted with 2-space indentation | ||
| return c.json(data); | ||
| }); | ||
|
|
||
| export default app; | ||
| ``` | ||
|
|
||
| </TabItem> <TabItem label="Python" icon="seti:python"> | ||
|
|
||
| ```py | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,34 @@ export default { | |
| } satisfies ExportedHandler; | ||
| ``` | ||
|
|
||
| </TabItem> <TabItem label="Hono" icon="seti:typescript"> | ||
|
|
||
| ```ts | ||
| import { Hono } from 'hono'; | ||
|
|
||
| type Bindings = {}; | ||
|
||
|
|
||
| const app = new Hono<{ Bindings: Bindings }>(); | ||
|
|
||
| app.get('*', async (c) => { | ||
| // someHost is set up to return JSON responses | ||
| const someHost = "https://jsonplaceholder.typicode.com"; | ||
| const url1 = someHost + "/todos/1"; | ||
| const url2 = someHost + "/todos/2"; | ||
|
|
||
| // Fetch both URLs concurrently | ||
| const responses = await Promise.all([fetch(url1), fetch(url2)]); | ||
|
|
||
| // Parse JSON responses concurrently | ||
| const results = await Promise.all(responses.map(r => r.json())); | ||
|
|
||
| // Return aggregated results | ||
| return c.json(results); | ||
| }); | ||
|
|
||
| export default app; | ||
| ``` | ||
|
|
||
| </TabItem> <TabItem label="Python" icon="seti:python"> | ||
|
|
||
| ```py | ||
|
|
||
coffee-mug marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -330,4 +330,108 @@ async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> { | |
| } | ||
| } | ||
| ``` | ||
| </TabItem> <TabItem label="Hono" icon="seti:typescript"> | ||
|
|
||
| ```ts | ||
| /** | ||
| * Shows how to restrict access using the HTTP Basic schema with Hono. | ||
| * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication | ||
| * @see https://tools.ietf.org/html/rfc7617 | ||
| */ | ||
|
|
||
| import { Hono } from 'hono'; | ||
|
||
| import { auth } from 'hono/basic-auth'; | ||
coffee-mug marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import { Buffer } from "node:buffer"; | ||
|
|
||
| // Define environment interface | ||
| interface Env { | ||
| Bindings: { | ||
| PASSWORD: string; | ||
| }; | ||
| } | ||
|
|
||
| const app = new Hono<Env>(); | ||
|
|
||
| // Helper function for comparing strings safely | ||
| const encoder = new TextEncoder(); | ||
| function timingSafeEqual(a: string, b: string) { | ||
| const aBytes = encoder.encode(a); | ||
| const bBytes = encoder.encode(b); | ||
|
|
||
| if (aBytes.byteLength !== bBytes.byteLength) { | ||
| // Strings must be the same length in order to compare | ||
| // with crypto.subtle.timingSafeEqual | ||
| return false; | ||
| } | ||
|
|
||
| return crypto.subtle.timingSafeEqual(aBytes, bBytes); | ||
| } | ||
|
|
||
| // Public homepage - accessible to everyone | ||
| app.get('/', (c) => { | ||
| return c.text("Anyone can access the homepage."); | ||
| }); | ||
|
|
||
| // Logout route | ||
| app.get('/logout', (c) => { | ||
| // Invalidate the "Authorization" header by returning a HTTP 401. | ||
| // We do not send a "WWW-Authenticate" header, as this would trigger | ||
| // a popup in the browser, immediately asking for credentials again. | ||
| return c.text("Logged out.", 401); | ||
| }); | ||
|
|
||
| // Admin route - protected with Basic Auth | ||
| app.get('/admin', async (c) => { | ||
| const BASIC_USER = "admin"; | ||
|
|
||
| // You will need an admin password. This should be | ||
| // attached to your Worker as an encrypted secret. | ||
| // Refer to https://developers.cloudflare.com/workers/configuration/secrets/ | ||
| const BASIC_PASS = c.env.PASSWORD ?? "password"; | ||
|
|
||
| // The "Authorization" header is sent when authenticated | ||
| const authorization = c.req.header('Authorization'); | ||
| if (!authorization) { | ||
| // Prompts the user for credentials | ||
| return c.text("You need to login.", 401, { | ||
| 'WWW-Authenticate': 'Basic realm="my scope", charset="UTF-8"', | ||
| }); | ||
| } | ||
|
|
||
| const [scheme, encoded] = authorization.split(" "); | ||
|
|
||
| // The Authorization header must start with Basic, followed by a space | ||
| if (!encoded || scheme !== "Basic") { | ||
| return c.text("Malformed authorization header.", 400); | ||
| } | ||
|
|
||
| const credentials = Buffer.from(encoded, "base64").toString(); | ||
|
|
||
| // The username & password are split by the first colon | ||
| //=> example: "username:password" | ||
| const index = credentials.indexOf(":"); | ||
| const user = credentials.substring(0, index); | ||
| const pass = credentials.substring(index + 1); | ||
|
|
||
| if (!timingSafeEqual(BASIC_USER, user) || !timingSafeEqual(BASIC_PASS, pass)) { | ||
| // Prompts the user for credentials again | ||
| return c.text("You need to login.", 401, { | ||
| 'WWW-Authenticate': 'Basic realm="my scope", charset="UTF-8"', | ||
| }); | ||
| } | ||
|
|
||
| // Success! User is authenticated | ||
| return c.text("🎉 You have private access!", 200, { | ||
| 'Cache-Control': 'no-store', | ||
| }); | ||
| }); | ||
|
|
||
| // Handle 404 for any other routes | ||
| app.notFound((c) => { | ||
| return c.text("Not Found.", 404); | ||
| }); | ||
|
|
||
| export default app; | ||
| ``` | ||
|
|
||
| </TabItem> </Tabs> | ||
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.
This is like a nitpick, but you can use
c.html():