|
1 | 1 | /** |
2 | | - * Local development server for the API with Neon database support via Hyperdrive. |
| 2 | + * Local development server that emulates Cloudflare Workers runtime with Neon database. |
3 | 3 | * |
4 | | - * @remarks |
5 | | - * This file bootstraps a local Cloudflare Workers environment using Wrangler's |
6 | | - * getPlatformProxy, allowing you to develop against a Neon database instance |
7 | | - * locally with Hyperdrive bindings. |
8 | | - * |
9 | | - * Features: |
10 | | - * - Emulates Cloudflare Workers runtime environment |
11 | | - * - Provides access to Hyperdrive bindings for Neon PostgreSQL |
12 | | - * - Connection pooling via Hyperdrive |
13 | | - * - Hot reloading support for rapid development |
| 4 | + * WARNING: This file uses getPlatformProxy which requires wrangler.jsonc configuration. |
| 5 | + * Hyperdrive bindings must be configured for both HYPERDRIVE_CACHED and HYPERDRIVE_DIRECT. |
14 | 6 | * |
15 | 7 | * @example |
16 | | - * Start the development server: |
17 | 8 | * ```bash |
18 | 9 | * bun --filter @repo/api dev |
| 10 | + * bun --filter @repo/api dev --env=staging # Use staging environment config |
19 | 11 | * ``` |
20 | 12 | * |
21 | 13 | * SPDX-FileCopyrightText: 2014-present Kriasoft |
22 | 14 | * SPDX-License-Identifier: MIT |
23 | 15 | */ |
24 | 16 |
|
25 | 17 | import { Hono } from "hono"; |
| 18 | +import { parseArgs } from "node:util"; |
26 | 19 | import { getPlatformProxy } from "wrangler"; |
27 | 20 | import api from "./index.js"; |
28 | 21 | import { createAuth } from "./lib/auth.js"; |
29 | 22 | import type { AppContext } from "./lib/context.js"; |
30 | 23 | import { createDb } from "./lib/db.js"; |
31 | 24 | import type { Env } from "./lib/env.js"; |
32 | 25 |
|
| 26 | +const { values: args } = parseArgs({ |
| 27 | + args: Bun.argv.slice(2), |
| 28 | + options: { |
| 29 | + env: { type: "string" }, |
| 30 | + }, |
| 31 | +}); |
| 32 | + |
33 | 33 | type CloudflareEnv = { |
34 | 34 | HYPERDRIVE_CACHED: Hyperdrive; |
35 | 35 | HYPERDRIVE_DIRECT: Hyperdrive; |
36 | 36 | } & Env; |
37 | 37 |
|
38 | | -/** |
39 | | - * Initialize the local development server with Cloudflare bindings. |
40 | | - */ |
| 38 | +// [INITIALIZATION] |
41 | 39 | const app = new Hono<AppContext>(); |
42 | 40 |
|
43 | | -/** |
44 | | - * Create a local Cloudflare environment proxy. |
45 | | - * |
46 | | - * @remarks |
47 | | - * - Reads configuration from wrangler.jsonc in the parent directory |
48 | | - * - Enables persistence to maintain D1 database state across restarts |
49 | | - * - Provides access to all Cloudflare bindings defined in wrangler.jsonc |
50 | | - */ |
| 41 | +// NOTE: persist:true maintains D1 state across restarts (.wrangler directory) |
| 42 | +// Environment defaults to 'dev' unless --env flag is provided |
51 | 43 | const cf = await getPlatformProxy<CloudflareEnv>({ |
52 | 44 | configPath: "./wrangler.jsonc", |
| 45 | + environment: args.env ?? "dev", |
53 | 46 | persist: true, |
54 | 47 | }); |
55 | 48 |
|
56 | | -/** |
57 | | - * Middleware to inject database binding into request context. |
58 | | - * |
59 | | - * @remarks |
60 | | - * Uses Neon PostgreSQL via Hyperdrive for both local development |
61 | | - * and production deployment with connection pooling and caching. |
62 | | - */ |
| 49 | +// [CONTEXT INJECTION] |
| 50 | +// Creates two database connections per request: |
| 51 | +// - db: Uses Hyperdrive caching (read-heavy queries) |
| 52 | +// - dbDirect: Bypasses cache (write operations, transactions) |
63 | 53 | app.use("*", async (c, next) => { |
64 | 54 | const db = createDb(cf.env.HYPERDRIVE_CACHED); |
65 | 55 | const dbDirect = createDb(cf.env.HYPERDRIVE_DIRECT); |
| 56 | + |
| 57 | + // Priority: Cloudflare bindings > process.env > empty string |
| 58 | + // Required for local dev where secrets aren't in wrangler.jsonc |
| 59 | + const secretKeys = [ |
| 60 | + "BETTER_AUTH_SECRET", |
| 61 | + "GOOGLE_CLIENT_ID", |
| 62 | + "GOOGLE_CLIENT_SECRET", |
| 63 | + "OPENAI_API_KEY", |
| 64 | + ] as const; |
| 65 | + |
| 66 | + const env = { |
| 67 | + ...cf.env, |
| 68 | + ...Object.fromEntries( |
| 69 | + secretKeys.map((key) => [key, (cf.env[key] || process.env[key]) ?? ""]), |
| 70 | + ), |
| 71 | + APP_NAME: cf.env.APP_NAME || process.env.APP_NAME || "Example", |
| 72 | + APP_ORIGIN: |
| 73 | + // Prefer origin set by `apps/app` at runtime |
| 74 | + c.req.header("x-forwarded-origin") || |
| 75 | + c.env.APP_ORIGIN || |
| 76 | + process.env.APP_ORIGIN || |
| 77 | + "http://localhost:5173", |
| 78 | + }; |
| 79 | + |
66 | 80 | c.set("db", db); |
67 | 81 | c.set("dbDirect", dbDirect); |
68 | | - c.set("auth", createAuth(db, cf.env)); |
| 82 | + c.set("auth", createAuth(db, env)); |
69 | 83 | await next(); |
70 | 84 | }); |
71 | 85 |
|
72 | | -/** |
73 | | - * Mount the main API routes. |
74 | | - * All routes defined in ./lib/app.ts will be available at the root path. |
75 | | - */ |
| 86 | +// Routes from ./index.js mounted at root |
76 | 87 | app.route("/", api); |
77 | 88 |
|
78 | 89 | export default app; |
0 commit comments