-
Notifications
You must be signed in to change notification settings - Fork 1
Align guide docs with the code on main #373
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -27,10 +27,11 @@ crates/my-app-adapter-axum/ | |
| The Axum entrypoint wires the adapter: | ||
|
|
||
| ```rust | ||
| use edgezero_adapter_axum::dev_server::run_app; | ||
| use my_app_core::App; | ||
|
|
||
| fn main() -> anyhow::Result<()> { | ||
| edgezero_adapter_axum::run_app::<App>() | ||
| run_app::<App>() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⛏ At if !A::owns_logging() { … }The Logging section this PR added at |
||
| } | ||
| ``` | ||
|
|
||
|
|
@@ -82,20 +83,26 @@ The binary is placed in `target/release/my-app-adapter-axum`. | |
| The Axum adapter provides a native HTTP client for proxying: | ||
|
|
||
| ```rust | ||
| use edgezero_adapter_axum::AxumProxyClient; | ||
| use edgezero_adapter_axum::proxy::AxumProxyClient; | ||
| use edgezero_core::proxy::ProxyService; | ||
|
|
||
| let client = AxumProxyClient::default(); | ||
| let client = AxumProxyClient::try_new()?; | ||
| let response = ProxyService::new(client).forward(request).await?; | ||
| ``` | ||
|
|
||
| This uses `reqwest` under the hood for outbound HTTP requests. | ||
| This uses `reqwest` under the hood for outbound HTTP requests. `try_new` is | ||
| fallible because it builds a `reqwest::Client`; it returns a `reqwest::Error` if | ||
| the TLS backend cannot be initialised on the host. | ||
|
|
||
| ## Logging | ||
|
|
||
| The Axum adapter's `run_app` helper installs `simple_logger` and reads logging configuration | ||
| from `edgezero.toml` (level and `echo_stdout`). If you want a different logger, wire your own | ||
| entrypoint using `App::build_app()` and `AxumDevServer`. | ||
| The Axum adapter's `run_app` helper installs `simple_logger` at the level read from | ||
| `EDGEZERO__LOGGING__LEVEL`, falling back to `info` when the variable is unset or | ||
| unparseable. It does not read `edgezero.toml`, and `echo_stdout` has no effect on | ||
| the runtime. To install a different logger, set `owns_logging = true` on your `app!` | ||
| declaration so `run_app` skips its own logger, then install yours in `main`. Wiring | ||
| `App::build_app()` and `AxumDevServer` by hand remains the fallback if you also need | ||
| to control the bind address or store setup. | ||
|
|
||
| ::: tip Logging status | ||
| `run_app` wires logging automatically; custom entrypoints should install a logger explicitly. | ||
|
|
@@ -136,6 +143,31 @@ cargo test -p my-app-core | |
| cargo test -p my-app-adapter-axum | ||
| ``` | ||
|
|
||
| ## KV Storage | ||
|
|
||
| Each declared `[stores.kv]` id resolves to a `redb`-backed store on disk under | ||
| `.edgezero/`, so values persist across dev-server restarts. The file name is | ||
| derived from the platform store name, which comes from | ||
| `EDGEZERO__STORES__KV__<ID>__NAME` or defaults to the logical id: | ||
|
|
||
| ``` | ||
| .edgezero/kv-<slug>-<hash>.redb | ||
| ``` | ||
|
|
||
| The database file grows over time and does not shrink after deletions. To reclaim | ||
| space, delete the file in `.edgezero/`; the data is lost. See [KV Storage](/guide/kv) | ||
| for the portable API. | ||
|
|
||
| ## Secret Store | ||
|
|
||
| A declared `[stores.secrets]` id resolves to an `EnvSecretStore`, which looks up each | ||
| secret name verbatim in the process environment. Axum lists `secrets` in its | ||
| `single_store_kinds`, so only one secrets id may be declared: | ||
|
|
||
| ```bash | ||
| API_KEY=mysecret edgezero serve --adapter axum | ||
| ``` | ||
|
|
||
| ## Config Store | ||
|
|
||
| For local development, each declared `[stores.config]` id resolves to a | ||
|
|
@@ -205,8 +237,14 @@ CMD ["my-app-adapter-axum"] | |
| Configure the Axum adapter in `edgezero.toml`. See [Configuration](/guide/configuration) for the full | ||
| manifest reference. | ||
|
|
||
| The `axum.toml` file is used by the Axum CLI helper to locate the crate and display the port. | ||
| The runtime currently binds to `127.0.0.1:8787` regardless of the `axum.toml` port value. | ||
| The `axum.toml` file is used by the Axum CLI helper to locate the crate and carry a | ||
| default port. `edgezero serve --adapter axum` resolves the bind address with this | ||
| precedence, highest first: the `EDGEZERO__ADAPTER__HOST` / `EDGEZERO__ADAPTER__PORT` | ||
| environment variables, then `[adapters.axum.adapter]` in `edgezero.toml`, then | ||
| `axum.toml`, then `127.0.0.1:8787`. The CLI passes the resolved address to the child | ||
| process as `EDGEZERO__ADAPTER__HOST` / `EDGEZERO__ADAPTER__PORT`. Running the binary | ||
| directly bypasses that resolution: it reads only those two environment variables and | ||
| otherwise falls back to `127.0.0.1:8787`. | ||
|
|
||
| ## Development Workflow | ||
|
|
||
|
|
@@ -231,7 +269,7 @@ A typical development workflow: | |
| | Concurrency | Multi-threaded | Single-threaded | | ||
|
|
||
| ::: tip Development Parity | ||
| While Axum provides a convenient development environment, always test on actual edge platforms before deploying. Some edge-specific features (KV stores, geolocation) aren't available in the Axum adapter. | ||
| While Axum provides a convenient development environment, always test on actual edge platforms before deploying. Provider-specific behaviour such as store backends and request context differs on the real targets. | ||
| ::: | ||
|
|
||
| ## Next Steps | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ Deploy EdgeZero applications to Cloudflare Workers using WebAssembly. | |
| ## Prerequisites | ||
|
|
||
| - [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/) | ||
| - worker-builder: `cargo install worker-builder` | ||
| - worker-build: `cargo install worker-build` | ||
| - Rust `wasm32-unknown-unknown` target: `rustup target add wasm32-unknown-unknown` | ||
|
|
||
| ## Project Setup | ||
|
|
@@ -28,10 +28,10 @@ The Wrangler manifest configures your Worker: | |
| ```toml | ||
| name = "my-app" | ||
| main = "build/worker/shim.mjs" | ||
| compatibility_date = "2024-01-01" | ||
| compatibility_date = "2023-05-01" | ||
|
|
||
| [build] | ||
| command = "edgezero build --adapter cloudflare" | ||
| command = "worker-build --release" | ||
| ``` | ||
|
|
||
| ### Entrypoint | ||
|
|
@@ -56,10 +56,35 @@ derived from the baked store ids and queried individually). Per-id | |
| request extensions automatically. No `edgezero.toml` is loaded by | ||
| the runtime — see [the migration guide](../manifest-store-migration.md). | ||
|
|
||
| The low-level `dispatch()` helper remains available only for fully manual wiring and does not inject | ||
| store metadata. Prefer `run_app` or `dispatch_with_config` for normal use. | ||
| `dispatch_with_config_handle` exists for advanced/manual cases where you already have a prepared | ||
| `ConfigStoreHandle`. | ||
| For fully manual wiring, `CloudflareService::new(&app)` builds a dispatcher one | ||
| store at a time: `.with_config(binding)` (a KV binding name), | ||
| `.with_config_handle(handle)`, `.with_kv(binding)`, `.with_secrets()`, the | ||
| matching `.require_kv()` / `.require_secrets()` flags, and finally | ||
| `.dispatch(req, env, ctx).await`, which needs the worker `Env` and `Context` | ||
| to open bindings: | ||
|
|
||
| ```rust | ||
| use edgezero_adapter_cloudflare::request::CloudflareService; | ||
| use edgezero_core::app::Hooks as _; | ||
| use my_app_core::App; | ||
| use worker::*; | ||
|
|
||
| #[event(fetch)] | ||
| pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> { | ||
| let app = App::build_app(); | ||
| CloudflareService::new(&app) | ||
| .with_config("APP_CONFIG") | ||
| .with_kv("APP_KV") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⛏ Uppercase reads like a Cloudflare naming convention EdgeZero never emits. Suggest |
||
| .dispatch(req, env, ctx) | ||
| .await | ||
| } | ||
| ``` | ||
|
|
||
| This path takes bindings verbatim and does not resolve `EDGEZERO__STORES__*` | ||
| selectors, so prefer `run_app` unless you are mocking a backend. | ||
| `run_app` dispatches through an internal registry-based path; unlike Fastly's | ||
| `dispatch_with_registries`, it is not part of the Cloudflare adapter's public | ||
| API. | ||
|
|
||
| ## Building | ||
|
|
||
|
|
@@ -98,7 +123,7 @@ wrangler deploy --cwd crates/my-app-adapter-cloudflare | |
| Cloudflare Workers use the global `fetch` API for outbound requests: | ||
|
|
||
| ```rust | ||
| use edgezero_adapter_cloudflare::CloudflareProxyClient; | ||
| use edgezero_adapter_cloudflare::proxy::CloudflareProxyClient; | ||
| use edgezero_core::proxy::ProxyService; | ||
|
|
||
| let client = CloudflareProxyClient; | ||
|
|
@@ -124,7 +149,7 @@ Access Cloudflare-specific APIs via the request context extensions: | |
|
|
||
| ```rust | ||
| use edgezero_core::context::RequestContext; | ||
| use edgezero_adapter_cloudflare::CloudflareRequestContext; | ||
| use edgezero_adapter_cloudflare::context::CloudflareRequestContext; | ||
|
|
||
| async fn handler(ctx: RequestContext) -> Result<Response, EdgeError> { | ||
| if let Some(cf_ctx) = CloudflareRequestContext::get(ctx.request()) { | ||
|
|
@@ -174,11 +199,37 @@ id = "abc123…" | |
|
|
||
| The binding name comes from `EDGEZERO__STORES__CONFIG__APP_CONFIG__NAME` | ||
| (defaulting to the logical id `app_config` when unset). Populate the | ||
| namespace via `wrangler kv:key put`. Missing bindings log a one-time | ||
| namespace via `wrangler kv key put`. Missing bindings log a one-time | ||
| warning and the id is dropped from the registry. See | ||
| [the migration guide](../manifest-store-migration.md) if you are coming | ||
| from the pre-rewrite `[vars]`-backed JSON-string form. | ||
|
|
||
| KV and config share the same `[[kv_namespaces]]` binding space on Cloudflare, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 This new section is right, which leaves "Environment Variables & Secrets" at This section correctly says handlers read secrets through the Suggest folding |
||
| so the same logical id must not appear under both `[stores.kv]` and | ||
| `[stores.config]`; both would resolve to a single underlying namespace at | ||
| runtime. `edgezero config validate` rejects the collision. | ||
|
|
||
| ## Secret Store | ||
|
|
||
| Worker Secrets is a single flat bag with no namespace concept, so exactly one | ||
| `[stores.secrets]` id is permitted; `edgezero config validate --strict` rejects | ||
| more than one. Handlers read values through the `Secrets` extractor or | ||
| `ctx.secret_store(id)`, and a secret with no matching binding resolves to `None` | ||
| rather than erroring. | ||
|
|
||
| ```toml | ||
| # edgezero.toml | ||
| [stores.secrets] | ||
| ids = ["default"] | ||
| ``` | ||
|
|
||
| Populate secrets with the Wrangler CLI; there is no binding flag, since the | ||
| secret name is the binding: | ||
|
|
||
| ```bash | ||
| wrangler secret put API_KEY | ||
| ``` | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 The KV Storage section immediately below ( It tells the reader to write The Config Store section at |
||
| ## KV Storage | ||
|
|
||
| Use Cloudflare KV for edge storage: | ||
|
|
||
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.
🤔 The rewritten comment is better on the filename, but keeps two claims the Axum backend contradicts.
It says expired keys "are removed on the next
get_bytescall for that key", and that keys never accessed "remain in the database until deleted, so the store's … file grows without bound".list_keysalso sweeps: it collectsexpired_keysduring the range scan and callsself.cleanup_expired_keys(&expired_keys)?(crates/edgezero-adapter-axum/src/key_value_store.rs:341-353). The type's own doc says as much — "lazily evicted (checked on read/list)" (same file:66).Fix: "…removed on the next
get_bytesfor that key, or by anylist_keysscan that reaches it. Keys neither read nor listed after expiration remain in the file."