|
| 1 | +# Plan: `cedar serve --ud` — Serve Both Web and API Sides |
| 2 | + |
| 3 | +**Date:** 2026-05-08 |
| 4 | +**Status:** Draft |
| 5 | +**Relates to:** Universal Deploy integration plan (Phases 3–6), Phase 6 Addendum |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Problem Statement |
| 10 | + |
| 11 | +Today, `cedar serve --ud` does not exist. The `--ud` flag is only wired to the |
| 12 | +`cedar serve api` sub-command. Running `cedar serve` (no side specified, the |
| 13 | +"both" case) always uses the legacy Fastify pair: a web Fastify server on |
| 14 | +`webPort` that proxies API calls to a separate API Fastify server on `apiPort`. |
| 15 | + |
| 16 | +The goal is to support `cedar serve --ud` (no side qualifier) for |
| 17 | +**local production-like testing** — i.e. verifying a `cedar build --ud` output |
| 18 | +behaves correctly before deploying. |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Command Roles |
| 23 | + |
| 24 | +It is worth being explicit about what each command is for: |
| 25 | + |
| 26 | +| Command | Purpose | |
| 27 | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | |
| 28 | +| `cedar serve api --ud` | **Production.** Runs `api/dist/ud/index.js` (srvx). Nginx, or some other reverse proxy, serves web assets separately. | |
| 29 | +| `cedar serve --ud` | **Local production-like testing.** Runs the UD API entry + a web static file server so the full app is usable in a browser. | |
| 30 | +| `cedar serve` (both) | **Legacy.** Fastify web (with proxy) + Fastify API. Unchanged. | |
| 31 | + |
| 32 | +In real baremetal/VPS production the topology is: |
| 33 | + |
| 34 | +- **nginx** serves `web/dist/` static files directly |
| 35 | +- **nginx** proxies API routes to a Node process (`api/dist/ud/index.js`) |
| 36 | + |
| 37 | +`cedar serve --ud` mirrors this as closely as possible locally: |
| 38 | + |
| 39 | +- **Fastify web server** (existing `redwoodFastifyWeb`) serves `web/dist/` |
| 40 | + and proxies API requests to the UD API process. Fastify is basically standing |
| 41 | + in for nginx |
| 42 | +- **Forked `api/dist/ud/index.js`** handles all API routes via srvx |
| 43 | + |
| 44 | +No changes to the Vite plugin or the build pipeline are needed. |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## Current State |
| 49 | + |
| 50 | +| Command | What happens today | |
| 51 | +| ---------------------- | -------------------------------------------------- | |
| 52 | +| `cedar serve api` | Fastify API server | |
| 53 | +| `cedar serve api --ud` | Forks `api/dist/ud/index.js` (srvx) | |
| 54 | +| `cedar serve web` | Fastify web server (`@cedarjs/web-server`) | |
| 55 | +| `cedar serve` (both) | Fastify web (with proxy) + Fastify API | |
| 56 | +| `cedar serve --ud` | Not recognised / falls through to "both" (Fastify) | |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +## Implementation Plan |
| 61 | + |
| 62 | +### Step 1 — Add `--ud` to the `$0` (both) sub-command |
| 63 | + |
| 64 | +**Files changed:** |
| 65 | + |
| 66 | +- `packages/cli/src/commands/serve.ts` |
| 67 | + |
| 68 | +**What to do:** |
| 69 | + |
| 70 | +1. Add `--ud` to the `$0` yargs sub-command's `builder`, mirroring the `api` |
| 71 | + sub-command: |
| 72 | + |
| 73 | + ```ts |
| 74 | + yargs.option('ud', { |
| 75 | + description: |
| 76 | + 'Use the Universal Deploy server (srvx) for the API side. ' + |
| 77 | + 'The web side is served by the existing static file server. ' + |
| 78 | + 'Pass --ud to opt in; the default is Fastify for both sides.', |
| 79 | + type: 'boolean', |
| 80 | + default: false, |
| 81 | + }) |
| 82 | + ``` |
| 83 | + |
| 84 | +2. In the `$0` handler, when `argv.ud` is `true`: |
| 85 | + |
| 86 | + a. **Validate build artifacts** — both must exist before starting: |
| 87 | + - `api/dist/ud/index.js` — if missing, print a clear error pointing to |
| 88 | + `yarn cedar build --ud` and exit. |
| 89 | + - `web/dist/index.html` — if missing, print a clear error pointing to |
| 90 | + `yarn cedar build` and exit. |
| 91 | + |
| 92 | + b. **Resolve ports** — use the same helpers as the existing both-handler: |
| 93 | + - Web: `getWebHost()` / `getWebPort()` (default `8910`) |
| 94 | + - API: `getAPIHost()` / `getAPIPort()` (default `8911`) |
| 95 | + |
| 96 | + c. **Start the Fastify web server** with `apiProxyTarget` pointing at the UD |
| 97 | + API port — exactly as the existing `bothCLIConfigHandler` does, just |
| 98 | + substituting the forked UD entry for the Fastify API server. |
| 99 | + |
| 100 | + d. **Fork `api/dist/ud/index.js`** — same pattern as `cedar serve api --ud`, |
| 101 | + passing `--port` / `--host` for the API port/host. |
| 102 | + |
| 103 | + e. **Print both addresses** once both are up. |
| 104 | + |
| 105 | +3. When `argv.ud` is `false`, the existing Fastify-pair behaviour is completely |
| 106 | + unchanged. |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +### Step 2 — Update the build-artifact middleware check |
| 111 | + |
| 112 | +The existing `serve.ts` middleware already validates that the relevant dist |
| 113 | +directories exist before any sub-command handler runs. Extend it so that when |
| 114 | +`--ud` is present in the default (both) case, it also checks for |
| 115 | +`api/dist/ud/index.js` and emits a helpful error pointing to |
| 116 | +`yarn cedar build --ud` rather than the generic `yarn cedar build`. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +### Step 3 — Update error messages |
| 121 | + |
| 122 | +The build-not-found error in the `--ud` path (both Step 1 and the existing |
| 123 | +`api --ud` handler) should mention `yarn cedar build --ud` explicitly, not just |
| 124 | +`yarn cedar build api`, so users understand a plain build is not sufficient. |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +## Affected Files Summary |
| 129 | + |
| 130 | +File: `packages/cli/src/commands/serve.ts` |
| 131 | + |
| 132 | +Change: Add `--ud` to `$0` builder; add UD both-sides handler in `$0` handler; |
| 133 | +update middleware check; update error messages |
| 134 | + |
| 135 | +That is the only file that needs to change. |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Sequencing |
| 140 | + |
| 141 | +All three steps are in the same file and can land in one PR. |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## What This Approach Does NOT Include |
| 146 | + |
| 147 | +- **`virtual:cedar-web` / web fallback inside the UD build artifact** — not |
| 148 | + needed here. That would only be relevant for single-runtime deployments (e.g. |
| 149 | + a single Cloudflare Worker serving both web and API). For the baremetal/nginx |
| 150 | + production topology that `cedar serve --ud` is modelling, web assets are |
| 151 | + always served by a separate process. |
| 152 | +- **Changes to `cedarUniversalDeployPlugin` or `buildUDApiServer`** — the |
| 153 | + build pipeline is unchanged. |
| 154 | +- **Single-port serving** — two ports (web on `8910`, API on `8911`) is |
| 155 | + correct here because it mirrors the two-process nginx topology. |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## Questions/Answers |
| 160 | + |
| 161 | +1. **`server.ts` / custom Fastify compatibility**: When `--ud` is passed, |
| 162 | + should Cedar warn if the project has a custom `api/src/server.ts`? That |
| 163 | + file is a Fastify concept and is silently ignored by the UD entry. A |
| 164 | + warning here would be better than a silent skip. |
| 165 | + |
| 166 | + Answer: Yes, Cedar should warn if the project has a custom |
| 167 | + `api/src/server.ts`. It should acknowledge that the user is testing the |
| 168 | + experimental UD support and that it won't match their production Fastify |
| 169 | + setup. |
| 170 | + |
| 171 | +2. **Port flags**: The `$0` sub-command currently accepts `--port` (single |
| 172 | + port for... something). For the `--ud` path, there are two ports. Should |
| 173 | + `--port` set the web port, the API port, or be disallowed in favour of |
| 174 | + `--web-port` / `--api-port`? The existing `bothCLIConfig` already has |
| 175 | + `--webPort` and `--apiPort` options, so those should be used. |
| 176 | + |
| 177 | + Answer: For `yarn cedar serve --ud`, the `--port` flag should be disallowed. |
| 178 | + |
| 179 | +--- |
| 180 | + |
| 181 | +## Exit Criteria |
| 182 | + |
| 183 | +- `yarn cedar build --ud && yarn cedar serve --ud` starts two processes: |
| 184 | + the web server on `8910` and the backend on `8911`. |
| 185 | +- The web server proxies API requests to the UD API entry. |
| 186 | +- `GET /` (and all SPA routes) returns `web/dist/index.html`. |
| 187 | +- `GET /.api/functions/graphql` is proxied to the UD API entry and handled by |
| 188 | + Yoga. |
| 189 | +- `GET /.api/functions/myFunction` is proxied to the UD API entry and handled by |
| 190 | + the function handler. |
| 191 | +- Without `--ud`, all existing `cedar serve` behavior is unchanged. |
| 192 | +- `cedar serve --ud` with a missing `api/dist/ud/index.js` prints a clear error |
| 193 | + pointing to `yarn cedar build --ud`. |
0 commit comments